Contact: fumanchu@aminus.org

Log in as guest/geniusql to create tickets

Changeset 6

Show
Ignore:
Timestamp:
02/12/07 22:34:25
Author:
fumanchu
Message:

Added 'pytype' attribute to Columns:

  1. Added select_all, select_one methods to Table (which handle imperfect sql).
  2. Database.insert now coerces returned ID's to Python types.
  3. Database.select now returns an iterable (a ResultSet? object) that has an "imperfect" attribute.
  4. Replaced SelectWriter?.colnames with SelectWriter?.columns, a list of 3-tuples: (table, col, qname).

Also renamed Database.col_def to "columnclause".

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/geniusql/__init__.py

    r5 r6  
    129129    name: the SQL name for this table (unquoted). 
    130130    qname: the SQL name for this table (quoted). 
     131    pytype: the Python type (the actual type object, not its name). 
    131132    dbtype: the database type name (as used in a CREATE TABLE statement). 
    132     default: default value for this column for new rows. 
     133    default: default Python value for this column for new rows. 
    133134    hints: a dict of implementation hints, such as precision, scale, or bytes. 
    134135    key: True if this column is part of the table's primary key. 
     
    143144    """ 
    144145     
    145     def __init__(self, dbtype, default=None, hints=None, key=False, 
     146    def __init__(self, pytype, dbtype, default=None, hints=None, key=False, 
    146147                 name=None, qname=None): 
     148        self.pytype = pytype 
    147149        self.dbtype = dbtype 
    148150        self.name = name 
     
    164166     
    165167    def __repr__(self): 
    166         return ("%s.%s(%r, %r, dbtype=%r, default=%r, hints=%r, key=%r)" % 
     168        return ("%s.%s(%r, %r, default=%r, hints=%r, key=%r, name=%r, qname=%r)" % 
    167169                (self.__module__, self.__class__.__name__, 
    168                  self.name, self.qname, self.dbtype, 
    169                  self.default, self.hints, self.key) 
     170                 self.pytype, self.dbtype, 
     171                 self.default, self.hints, self.key, 
     172                 self.name, self.qname) 
    170173                ) 
    171174     
    172175    def __copy__(self): 
    173         newcol = self.__class__(self.name, self.qname, self.dbtype, 
    174                                 self.default, self.hints.copy(), self.key) 
     176        newcol = self.__class__(self.pytype, self.dbtype, 
     177                                self.default, self.hints, self.key, 
     178                                self.name, self.qname) 
    175179        newcol.autoincrement = self.autoincrement 
    176180        newcol.initial = self.initial 
     
    245249    def _add_column(self, column): 
    246250        """Internal function to add the column to the database.""" 
    247         coldef = self.db.col_def(column) 
     251        coldef = self.db.columnclause(column) 
    248252        self.db.execute("ALTER TABLE %s ADD COLUMN %s;" % (self.qname, coldef)) 
    249253     
     
    337341        self.indices[columnkey] = i 
    338342        return i 
     343     
     344    def select_all(self, restriction=None): 
     345        """Yield data dicts matching the given restriction.""" 
     346        attrs = self.keys() 
     347        data = self.db.select(self, attrs, restriction) 
     348        for row in data: 
     349            row = dict(zip(attrs, row)) 
     350            if restriction and data.imperfect: 
     351                # Run a dummy object through our restriction before yielding. 
     352                if not restriction(ImperfectDummy(**row)): 
     353                    continue 
     354            yield row 
     355     
     356    def select_one(self, restriction=None): 
     357        """Return a single data dict matching the given restriction (or None).""" 
     358        try: 
     359            return self.select_all(restriction).next() 
     360        except StopIteration: 
     361            return None 
     362 
     363 
     364class ImperfectDummy(object): 
     365    """A dummy object for resolving imperfect queries.""" 
     366    def __init__(self, **kwargs): 
     367        for k, v in kwargs.iteritems(): 
     368            setattr(self, k, v) 
    339369 
    340370 
     
    362392     
    363393    selectwriter = SelectWriter 
    364     def select(self, *args, **kwargs): 
    365         return self.selectwriter(self, *args, **kwargs) 
    366      
    367394    tableclass = Table 
    368395     
     
    536563    #                              Container                              # 
    537564     
    538     def col_def(self, column): 
     565    def columnclause(self, column): 
    539566        """Return a clause for the given column for CREATE or ALTER TABLE. 
    540567         
     
    581608                    self.create_sequence(table, column) 
    582609                 
    583                 fields.append(self.col_def(column)) 
     610                fields.append(self.columnclause(column)) 
    584611                if column.key: 
    585612                    pk.append(column.qname) 
     
    671698        return self.sql_name(columnkey) 
    672699     
    673     def column(self, pytype=unicode, default=None, hints=None, 
     700    def column(self, pytype=unicode, dbtype=None, default=None, hints=None, 
    674701               key=False, autoincrement=False): 
    675702        """Return a Column object from the given arguments.""" 
    676         col = Column(None, default, hints) 
    677         col.dbtype = self.typeadapter.coerce(col, pytype) 
     703        col = Column(pytype, dbtype, default, hints) 
     704        col.key = key 
     705        col.autoincrement = autoincrement 
     706         
     707        if dbtype is None: 
     708            col.dbtype = self.typeadapter.coerce(col, pytype) 
    678709        pytype2 = self.python_type(col.dbtype) 
    679710        col.imperfect_type = not self.isrelatedtype(pytype, pytype2) 
    680         col.key = key 
    681         col.autoincrement = autoincrement 
     711         
    682712        return col 
    683713     
     
    720750     
    721751    def execute(self, query, conn=None): 
    722         """execute(query, conn=None) -> result set.""" 
     752        """Return a native response for the given query.""" 
    723753        if conn is None: 
    724754            conn = self.connection() 
     
    739769        res = self.execute(query, conn) 
    740770        return res.row_list, res.col_defs 
     771     
     772    def select(self, relation, attributes, restriction=None, distinct=False): 
     773        """Yield matching data, coerced to Python types (where known).""" 
     774        sel = self.selectwriter(self, relation, attributes, restriction) 
     775        data, _ = self.fetch(sel.sql(distinct), self.get_transaction()) 
     776        return ResultSet(data, sel.columns, sel.imperfect) 
    741777     
    742778    def create_database(self): 
     
    919955        """Insert a row and return {idcolkey: newid}.""" 
    920956        t = self[tablekey] 
     957        coerce_out = self.adaptertosql.coerce 
     958        coerce_in = self.adapterfromdb.coerce 
    921959         
    922960        fields = [] 
     
    929967                continue 
    930968            if key in inputs: 
    931                 val = self.adaptertosql.coerce(inputs[key], col.dbtype) 
     969                val = coerce_out(inputs[key], col.dbtype) 
    932970                fields.append(col.qname) 
    933971                values.append(val) 
     
    938976        values = ", ".join(values) 
    939977        self.execute('INSERT INTO %s (%s) VALUES (%s);' % 
    940                         (t.qname, fields, values), transconn) 
     978                     (t.qname, fields, values), transconn) 
    941979         
    942980        if idkeys: 
    943             return self._grab_new_ids(t, idkeys, transconn) 
     981            newids = self._grab_new_ids(t, idkeys, transconn) 
     982            for key in newids.keys(): 
     983                col = t[key] 
     984                newids[key] = coerce_in(newids[key], col.dbtype, col.pytype) 
     985            return newids 
    944986        else: 
    945987            return {} 
    946988     
    947989    def _grab_new_ids(self, table, idkeys): 
     990        # Override this to fetch and return new autoincrement values. 
    948991        raise NotImplementedError 
    949992     
     
    9681011            self.execute(sql, self.get_transaction()) 
    9691012 
     1013 
     1014class ResultSet: 
     1015     
     1016    def __init__(self, data, columns, imperfect): 
     1017        self.data = data 
     1018        self.columns = columns 
     1019        self.imperfect = imperfect 
     1020        self.cursor = 0 
     1021     
     1022    def __iter__(self): 
     1023        return self 
     1024     
     1025    def next(self): 
     1026        try: 
     1027            row = self.data[self.cursor] 
     1028            self.cursor += 1 
     1029        except IndexError: 
     1030            raise StopIteration 
     1031         
     1032        coerced_row = [] 
     1033        for i, (table, col, qname) in enumerate(self.columns): 
     1034            val = row[i] 
     1035            if table and col: 
     1036                val = table.db.adapterfromdb.coerce(val, col.dbtype, col.pytype) 
     1037            coerced_row.append(val) 
     1038        return coerced_row 
     1039 
  • trunk/geniusql/providers/ado.py

    r5 r6  
    363363    def _add_column(self, column): 
    364364        """Internal function to add the column to the database.""" 
    365         coldef = self.db.col_def(column) 
     365        coldef = self.db.columnclause(column) 
    366366        # SQL Server doesn't use the "COLUMN" keyword with "ADD" 
    367367        self.db.execute("ALTER TABLE %s ADD %s;" % (self.qname, coldef)) 
     
    473473             
    474474            name = str(row[3]) 
    475             c = geniusql.Column(name, self.quote(name), dbtype, default, 
    476                                 key=(name in pknames)) 
     475            c = geniusql.Column(self.python_type(dbtype), dbtype, 
     476                                default, hints={}, key=(name in pknames), 
     477                                name=name, qname=self.quote(name)) 
    477478             
    478479            # This only works for SQL Server. The MSAccessDatabase will 
     
    893894            self.unlock() 
    894895     
    895     def col_def(self, column): 
     896    def columnclause(self, column): 
    896897        """Return a clause for the given column for CREATE or ALTER TABLE. 
    897898         
     
    11111112        return ADODatabase.python_type(self, dbtype) 
    11121113     
    1113     def col_def(self, column): 
     1114    def columnclause(self, column): 
    11141115        """Return a clause for the given column for CREATE or ALTER TABLE. 
    11151116         
  • trunk/geniusql/providers/firebird.py

    r5 r6  
    241241    def _add_column(self, column): 
    242242        """Internal function to add the column to the database.""" 
    243         coldef = self.db.col_def(column) 
     243        coldef = self.db.columnclause(column) 
    244244        # FB doesn't recognize the keyword "COLUMN" in "ADD". 
    245245        self.db.execute("ALTER TABLE %s ADD %s;" % (self.qname, coldef)) 
     
    362362                    default = float(default) 
    363363             
    364             # Column(name, qname, dbtype, default=None, hints=None, key=False) 
    365             col = geniusql.Column(name, self.quote(name), dbtype, default, hints, key) 
     364            # Column(pytype, dbtype, default=None, hints=None, key=False, name, qname) 
     365            col = geniusql.Column(self.python_type(dbtype), dbtype, default, 
     366                                  hints, key, name, self.quote(name)) 
    366367            cols.append(col) 
    367368        return cols 
     
    416417                        "to a Python type." % dbtype) 
    417418     
    418     def col_def(self, column): 
     419    def columnclause(self, column): 
    419420        """Return a clause for the given column for CREATE or ALTER TABLE. 
    420421         
     
    660661        """Insert a row and return {idcolkey: newid}.""" 
    661662        t = self[tablekey] 
     663        coerce_out = self.adaptertosql.coerce 
     664        coerce_in = self.adapterfromdb.coerce 
    662665         
    663666        newids = {} 
     
    669672                data, _ = self.fetch("SELECT GEN_ID(%s, 1) FROM RDB$DATABASE;" 
    670673                                     % col.sequence_name) 
    671                 val, = data[0] 
    672                 newids[key] = val 
     674                newid = coerce_in(data[0][0], col.dbtype, col.pytype) 
     675                newids[key] = newid 
    673676                 
    674                 val = self.adaptertosql.coerce(val, col.dbtype) 
     677                val = coerce_out(newid, col.dbtype) 
    675678                fields.append(col.qname) 
    676679                values.append(val) 
    677680            elif key in inputs: 
    678                 val = self.adaptertosql.coerce(inputs[key], col.dbtype) 
     681                val = coerce_out(inputs[key], col.dbtype) 
    679682                fields.append(col.qname) 
    680683                values.append(val) 
  • trunk/geniusql/providers/mysql.py

    r5 r6  
    215215        return "MySQL Version: %s" % self._version 
    216216     
    217     def col_def(self, column): 
     217    def columnclause(self, column): 
    218218        """Return a clause for the given column for CREATE or ALTER TABLE. 
    219219         
     
    246246        pk = [] 
    247247        for colkey, col in table.iteritems(): 
    248             fields.append(self.col_def(col)) 
     248            fields.append(self.columnclause(col)) 
    249249             
    250250            if col.autoincrement: 
     
    324324        cols = [] 
    325325        for row in data: 
    326             c = geniusql.Column(row[0], self.quote(row[0]), None, None) 
    327              
     326            hints = {} 
    328327            dbtype = row[1].upper() 
    329328            parenpos = dbtype.find("(") 
     
    333332                if baretype in ("DECIMAL", "NUMERIC"): 
    334333                    args = [x.strip() for x in args.split(",")] 
    335                     c.hints['precision'], c.hints['scale'] = args 
     334                    hints['precision'], hints['scale'] = args 
    336335                else: 
    337                     c.hints['bytes'] = args 
     336                    hints['bytes'] = args 
    338337            elif dbtype == "FLOAT": 
    339                 c.hints['precision'] = 24 
     338                hints['precision'] = 24 
    340339            elif dbtype.startswith("DOUBLE"): 
    341                 c.hints['precision'] = 53 
     340                hints['precision'] = 53 
    342341            elif dbtype in ("TINYBLOB", "TINYTEXT"): 
    343                 c.hints['bytes'] = (2 ** 8) - 1 
     342                hints['bytes'] = (2 ** 8) - 1 
    344343            elif dbtype in ("BLOB", "TEXT"): 
    345                 c.hints['bytes'] = (2 ** 16) - 1 
     344                hints['bytes'] = (2 ** 16) - 1 
    346345            elif dbtype in ("MEDIUMBLOB", "MEDIUMTEXT"): 
    347                 c.hints['bytes'] = (2 ** 24) - 1 
     346                hints['bytes'] = (2 ** 24) - 1 
    348347            elif dbtype in ("LONGBLOB", "LONGTEXT"): 
    349                 c.hints['bytes'] = (2 ** 32) - 1 
    350              
    351             c.key = (row[3] == "PRI") 
     348                hints['bytes'] = (2 ** 32) - 1 
     349             
     350            key = (row[3] == "PRI") 
     351            pytype = self.python_type(dbtype) 
     352             
     353            col = geniusql.Column(pytype, dbtype, None, hints, key, 
     354                                  row[0], self.quote(row[0])) 
    352355             
    353356            if row[4]: 
    354                 c.default = self.python_type(dbtype)(row[4]) 
    355              
     357                col.default = pytype(row[4]) 
    356358            if "auto_increment" in row[5].lower(): 
    357                 c.autoincrement = True 
    358              
    359             c.dbtype = dbtype 
    360              
    361             cols.append(c) 
     359                col.autoincrement = True 
     360             
     361            cols.append(col) 
    362362        return cols 
    363363     
  • trunk/geniusql/providers/psycopg.py

    r5 r6  
    216216            else: 
    217217                dbtype = None 
    218             c = geniusql.Column(row[0], self.quote(row[0]), dbtype, 
    219                                 key=row[2] in indices) 
     218            c = geniusql.Column(self.python_type(dbtype), dbtype, 
     219                                None, {}, row[2] in indices, 
     220                                row[0], self.quote(row[0])) 
    220221             
    221222            if dbtype in ('FLOAT4', 'FLOAT8'): 
     
    315316                        "to a Python type." % dbtype) 
    316317     
    317     def col_def(self, column): 
     318    def columnclause(self, column): 
    318319        """Return a clause for the given column for CREATE or ALTER TABLE. 
    319320         
  • trunk/geniusql/providers/pypgsql.py

    r5 r6  
    203203            else: 
    204204                dbtype = None 
    205             c = geniusql.Column(row[0], self.quote(row[0]), dbtype, 
    206                                 key=row[2] in indices) 
     205            c = geniusql.Column(self.python_type(dbtype), dbtype, 
     206                                None, {}, row[2] in indices, 
     207                                row[0], self.quote(row[0])) 
    207208             
    208209            if dbtype in ('FLOAT4', 'FLOAT8'): 
     
    302303                        "to a Python type." % dbtype) 
    303304     
    304     def col_def(self, column): 
     305    def columnclause(self, column): 
    305306        """Return a clause for the given column for CREATE or ALTER TABLE. 
    306307         
  • trunk/geniusql/providers/sqlite.py

    r5 r6  
    527527        cols = [] 
    528528        for row in data: 
    529             c = geniusql.Column(row[1], self.quote(row[1]), row[2].upper(), 
    530                                 default=row[4], key=bool(row[5])) 
     529            dbtype = row[2].upper() 
     530            c = geniusql.Column(self.python_type(dbtype), dbtype, 
     531                                row[4], {}, bool(row[5]), 
     532                                row[1], self.quote(row[1])) 
    531533             
    532534            # "A single row can hold up to 2 ** 30 bytes of data 
     
    621623        return {idkeys[0]: new_id} 
    622624     
    623     def col_def(self, column): 
     625    def columnclause(self, column): 
    624626        """Return a clause for the given column for CREATE or ALTER TABLE. 
    625627         
     
    652654        autoincr_col = None 
    653655        for col in table.itervalues(): 
    654             fields.append(self.col_def(col)) 
     656            fields.append(self.columnclause(col)) 
    655657             
    656658            if col.autoincrement: 
  • trunk/geniusql/select.py

    r5 r6  
    510510        self.whereclause, self.imperfect = self.where() 
    511511         
     512        self.columns = [] 
    512513        if self.attributes is None: 
    513514            # Return all columns 
    514             self.colnames = [] 
    515515            for t in self.tables: 
    516                 self.colnames.extend(self.columns(t, None)) 
     516                self.columns.extend(self._get_columns(t, None)) 
    517517        else: 
    518518            if isinstance(relation, Join): 
    519                 self.colnames = [] 
    520519                for t, attrs in zip(self.tables, self.attributes): 
    521                     self.colnames.extend(self.columns(t, attrs)) 
    522             else: 
    523                 self.colnames = [relation[a].qname for a in self.attributes] 
    524      
    525     def columns(self, tablewrapper, attrs=None): 
    526         """Return a list of quoted table.col names: ['"tbl"."col"', ...].""" 
     520                    self.columns.extend(self._get_columns(t, attrs)) 
     521            else: 
     522                for a in self.attributes: 
     523                    col = relation[a] 
     524                    self.columns.append((relation, col, col.qname)) 
     525     
     526    def _get_columns(self, tablewrapper, attrs=None): 
     527        """Return a list of (column, full quoted name) pairs.""" 
    527528        tablename = tablewrapper.alias or tablewrapper.qname 
     529        table = tablewrapper.table 
    528530        if attrs is None: 
    529531            # Place the identifier (primary key) properties first 
    530532            # in case others depend upon them. 
    531             cols = [(col.key, col.qname) for col 
    532                     in tablewrapper.table.itervalues()] 
     533            cols = [(col.key, col) for col in table.itervalues()] 
    533534            cols.sort() 
    534             cols = [qname for iskey, qname in cols] 
    535         else: 
    536             table = tablewrapper.table 
    537             cols = [table[a].qname for a in attrs] 
    538         return ['%s.%s' % (tablename, qname) for qname in cols] 
     535            return [(table, col, '%s.%s' % (tablename, col.qname)) 
     536                    for iskey, col in cols] 
     537        else: 
     538            cols = [] 
     539            for a in attrs: 
     540                col = table[a] 
     541                cols.append((table, col, '%s.%s' % (tablename, col.qname))) 
     542            return cols 
    539543     
    540544    def sql(self, distinct=False): 
     
    545549            d = '' 
    546550         
    547         cols = ', '.join(self.colnames
     551        cols = ', '.join([qname for t, c, qname in self.columns]
    548552         
    549553        w = self.whereclause 
  • trunk/geniusql/test/zoo_fixture.py

    r5 r6  
    257257    def test_3_Properties(self): 
    258258        # Zoos 
    259         sel = db.select(db['Zoo'], ('Founded', 'Opens', 'Admission'), 
    260                         logic.Expression(lambda z: z.Name == 'Wild Animal Park')) 
    261         data, _ = db.fetch(sel.sql(), db.get_transaction()) 
    262         self.assertEqual(data[0][0], datetime.date(2000, 1, 1)) 
    263         self.assertEqual(data[0][1], datetime.time(8, 15, 59)) 
    264         # This should have been updated when leopard.LastEscape was set. 
    265 ##        self.assertEqual(WAP.LastEscape, 
    266 ##                         datetime.datetime(2004, 12, 21, 8, 15, 0, 999907)) 
    267         self.assertEqual(data[0][2], 4.95) 
    268 ##             
    269 ##            SDZ = box.unit(Zoo, Founded=datetime.date(1835, 9, 13)) 
    270 ##            self.assertNotEqual(SDZ, None) 
    271 ##            self.assertEqual(SDZ.Founded, datetime.date(1835, 9, 13)) 
    272 ##            self.assertEqual(SDZ.Opens, datetime.time(9, 0, 0)) 
    273 ##            self.assertEqual(SDZ.LastEscape, None) 
    274 ##            self.assertEqual(float(SDZ.Admission), 0) 
    275 ##             
    276 ##            # Try a magic Sandbox recaller method 
    277 ##            Biodome = box.Zoo(Name = u'Montr\xe9al Biod\xf4me') 
    278 ##            self.assertNotEqual(Biodome, None) 
    279 ##            self.assertEqual(Biodome.Name, u'Montr\xe9al Biod\xf4me') 
    280 ##            self.assertEqual(Biodome.Founded, datetime.date(1992, 6, 19)) 
    281 ##            self.assertEqual(Biodome.Opens, datetime.time(9, 0, 0)) 
    282 ##            self.assertEqual(Biodome.LastEscape, None) 
    283 ##            self.assertEqual(float(Biodome.Admission), 11.75) 
    284 ##             
    285 ##            if typerefs.fixedpoint: 
    286 ##                seaworld = box.unit(Zoo, Admission = typerefs.fixedpoint.FixedPoint(60)) 
    287 ##            else: 
    288 ##                seaworld = box.unit(Zoo, Admission = float(60)) 
    289 ##            self.assertNotEqual(seaworld, None) 
    290 ##            self.assertEqual(seaworld.Name, u'Sea_World') 
    291 ##             
    292 ##            # Animals 
    293 ##            leopard = box.unit(Animal, Species='Leopard') 
    294 ##            self.assertEqual(leopard.Species, 'Leopard') 
    295 ##            self.assertEqual(leopard.Legs, 4) 
    296 ##            self.assertEqual(leopard.Lifespan, 73.5) 
    297 ##            self.assertEqual(leopard.ZooID, WAP.ID) 
    298 ##            self.assertEqual(leopard.PreviousZoos, None) 
    299 ##    ##        self.assertEqual(leopard.LastEscape, 
    300 ##    ##                         datetime.datetime(2004, 12, 21, 8, 15, 0, 999907)) 
    301 ##             
    302 ##            ostrich = box.unit(Animal, Species='Ostrich') 
    303 ##            self.assertEqual(ostrich.Species, 'Ostrich') 
    304 ##            self.assertEqual(ostrich.Legs, 2) 
    305 ##            self.assertEqual(ostrich.ZooID, None) 
    306 ##            self.assertEqual(ostrich.PreviousZoos, []) 
    307 ##            self.assertEqual(ostrich.LastEscape, None) 
    308 ##             
    309 ##            millipede = box.unit(Animal, Legs=1000000) 
    310 ##            self.assertEqual(millipede.Species, 'Millipede') 
    311 ##            self.assertEqual(millipede.Legs, 1000000) 
    312 ##            self.assertEqual(millipede.ZooID, SDZ.ID) 
    313 ##            self.assertEqual(millipede.PreviousZoos, [WAP.Name]) 
    314 ##            self.assertEqual(millipede.LastEscape, None) 
    315 ##             
    316 ##            # Test that strings in a list get decoded correctly. 
    317 ##            # See http://projects.amor.org/dejavu/ticket/50 
    318 ##            tiger = box.unit(Animal, Species='Tiger') 
    319 ##            self.assertEqual(tiger.PreviousZoos, ["animal\\universe"]) 
    320 ##             
    321 ##            # Test our 8000-byte limit. 
    322 ##            # len(pickle.dumps(["f" * (8000 - 14)]) == 8000 
    323 ##            slug = box.unit(Animal, Species='Slug') 
    324 ##            self.assertEqual(len(slug.PreviousZoos[0]), 8000 - 14) 
    325 ##             
    326 ##            # Exhibits 
    327 ##            exes = box.recall(Exhibit) 
    328 ##            self.assertEqual(len(exes), 2) 
    329 ##            if exes[0].Name == 'The Penguin Encounter': 
    330 ##                pe = exes[0] 
    331 ##                tr = exes[1] 
    332 ##            else: 
    333 ##                pe = exes[1] 
    334 ##                tr = exes[0] 
    335 ##            self.assertEqual(pe.ZooID, seaworld.ID) 
    336 ##            self.assertEqual(len(pe.Animals), 2) 
    337 ##            self.assertEqual(float(pe.Acreage), 3.1) 
    338 ##            self.assertEqual(pe.PettingAllowed, True) 
    339 ##            self.assertEqual(pe.Creators, (u'Richard F\xfcrst', u'Sonja Martin')) 
    340 ##             
    341 ##            self.assertEqual(tr.ZooID, SDZ.ID) 
    342 ##            self.assertEqual(len(tr.Animals), 1) 
    343 ##            self.assertEqual(float(tr.Acreage), 4) 
    344 ##            self.assertEqual(tr.PettingAllowed, False) 
    345 ##             
    346 ##        finally: 
    347 ##            box.flush_all() 
    348 ##     
     259        data = db.select(db['Zoo'], ('Founded', 'Opens', 'Admission'), 
     260                         logic.Expression(lambda z: z.Name == 'Wild Animal Park')) 
     261        wap = data.next() 
     262        self.assertEqual(wap[0], datetime.date(2000, 1, 1)) 
     263        self.assertEqual(wap[1], datetime.time(8, 15, 59)) 
     264        if typerefs.fixedpoint: 
     265            self.assertEqual(wap[2], typerefs.fixedpoint.FixedPoint("4.95")) 
     266        else: 
     267            self.assertEqual(wap[2], 4.95) 
     268         
     269        SDZ = db['Zoo'].select_one(lambda z: z.Founded == datetime.date(1835, 9, 13)) 
     270        self.assertEqual(SDZ['Founded'], datetime.date(1835, 9, 13)) 
     271        self.assertEqual(SDZ['Opens'], datetime.time(9, 0, 0)) 
     272        self.assertEqual(SDZ['LastEscape'], None) 
     273        self.assertEqual(float(SDZ['Admission']), 0) 
     274         
     275        Biodome = db['Zoo'].select_one(lambda z: z.Name == u'Montr\xe9al Biod\xf4me') 
     276        self.assertEqual(Biodome['Name'], u'Montr\xe9al Biod\xf4me') 
     277        self.assertEqual(Biodome['Founded'], datetime.date(1992, 6, 19)) 
     278        self.assertEqual(Biodome['Opens'], datetime.time(9, 0, 0)) 
     279        self.assertEqual(Biodome['LastEscape'], None) 
     280        self.assertEqual(float(Biodome['Admission']), 11.75) 
     281         
     282        if typerefs.fixedpoint: 
     283            seaworld = db['Zoo'].select_one(lambda z: z.Admission == 
     284                                            typerefs.fixedpoint.FixedPoint(60)) 
     285        else: 
     286            seaworld = db['Zoo'].select_one(lambda z: z.Admission == float(60)) 
     287        self.assertEqual(seaworld['Name'], u'Sea_World') 
     288##         
     289##        # Animals 
     290##        leopard = box.unit(Animal, Species='Leopard') 
     291##        self.assertEqual(leopard.Species, 'Leopard') 
     292##        self.assertEqual(leopard.Legs, 4) 
     293##        self.assertEqual(leopard.Lifespan, 73.5) 
     294##        self.assertEqual(leopard.ZooID, WAP.ID) 
     295##        self.assertEqual(leopard.PreviousZoos, None) 
     296####        self.assertEqual(leopard.LastEscape, 
     297####                         datetime.datetime(2004, 12, 21, 8, 15, 0, 999907)) 
     298##         
     299##        ostrich = box.unit(Animal, Species='Ostrich') 
     300##        self.assertEqual(ostrich.Species, 'Ostrich') 
     301##        self.assertEqual(ostrich.Legs, 2) 
     302##        self.assertEqual(ostrich.ZooID, None) 
     303##        self.assertEqual(ostrich.PreviousZoos, []) 
     304##        self.assertEqual(ostrich.LastEscape, None) 
     305##         
     306##        millipede = box.unit(Animal, Legs=1000000) 
     307##        self.assertEqual(millipede.Species, 'Millipede') 
     308##        self.assertEqual(millipede.Legs, 1000000) 
     309##        self.assertEqual(millipede.ZooID, SDZ.ID) 
     310##        self.assertEqual(millipede.PreviousZoos, [WAP.Name]) 
     311##        self.assertEqual(millipede.LastEscape, None) 
     312##         
     313##        # Test that strings in a list get decoded correctly. 
     314##        # See http://projects.amor.org/dejavu/ticket/50 
     315##        tiger = box.unit(Animal, Species='Tiger') 
     316##        self.assertEqual(tiger.PreviousZoos, ["animal\\universe"]) 
     317##         
     318##        # Test our 8000-byte limit. 
     319##        # len(pickle.dumps(["f" * (8000 - 14)]) == 8000 
     320##        slug = box.unit(Animal, Species='Slug') 
     321##        self.assertEqual(len(slug.PreviousZoos[0]), 8000 - 14) 
     322##         
     323##        # Exhibits 
     324##        exes = box.recall(Exhibit) 
     325##        self.assertEqual(len(exes), 2) 
     326##        if exes[0].Name == 'The Penguin Encounter': 
     327##            pe = exes[0] 
     328##            tr = exes[1] 
     329##        else: 
     330##            pe = exes[1] 
     331##            tr = exes[0] 
     332##        self.assertEqual(pe.ZooID, seaworld.ID) 
     333##        self.assertEqual(len(pe.Animals), 2) 
     334##        self.assertEqual(float(pe.Acreage), 3.1) 
     335##        self.assertEqual(pe.PettingAllowed, True) 
     336##        self.assertEqual(pe.Creators, (u'Richard F\xfcrst', u'Sonja Martin')) 
     337##         
     338##        self.assertEqual(tr.ZooID, SDZ.ID) 
     339##        self.assertEqual(len(tr.Animals), 1) 
     340##        self.assertEqual(float(tr.Acreage), 4) 
     341##        self.assertEqual(tr.PettingAllowed, False) 
     342##         
    349343##    def test_4_Expressions(self): 
    350344##        box = arena.new_sandbox()