Contact: fumanchu@aminus.org

Log in as guest/dejavu to create tickets

I think I've seen this ORM somewhere before...

Changeset 191

Show
Ignore:
Timestamp:
03/10/06 16:35:42
Author:
fumanchu
Message:

Fix for #49 (Unit._properties should be a list, not a dict). cls._properties no longer exists; use cls.properties (a list now, not a function) instead.

Files:

Legend:

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

    r190 r191  
    3333""" 
    3434 
    35 __version__ =  "1.4 beta
     35__version__ =  "1.4.0
    3636 
    3737 
  • trunk/doc/advanced.html

    r190 r191  
    257257        <tt>threading.Lock</tt>.</li> 
    258258    <li><b>save</b>: If <tt>not unit.dirty()</tt>, you can exit. Otherwise, 
    259         iterate through the Unit's properties() and persist each value, 
     259        iterate through the Unit's properties and persist each value, 
    260260        using an Adapter to coerce each value to the type expected by your 
    261261        database (or not, it's up to you; storeado special-cases 
     
    272272        than to make a separate call for each row.</li> 
    273273    <li><b>create_storage</b>: <tt>CREATE TABLE</tt> using 
    274         <tt>Unit.properties()</tt>, especially the <tt>UnitProperty.type</tt> 
     274        <tt>Unit.properties</tt>, especially the <tt>UnitProperty.type</tt> 
    275275        attribute. If <tt>UnitProperty.index == True</tt>, make a 
    276276        <tt>CREATE INDEX</tt> call.</li> 
  • trunk/storage/db.py

    r190 r191  
    11"""Base classes and tools for writing database Storage Managers. 
    2  
    3 Unit type -> [SQL repr ->] DB -> incoming Python value -> Unit type 
    4  
    52 
    63DATA TYPES 
    74========== 
     5Database Storage Manager modules are mostly adapters to support round-trip 
     6data coercion: 
     7 
     8Unit type -> [SQL repr ->] DB -> incoming Python value -> Unit type 
     9 
    810Since Dejavu relies on external database servers for its persistence, 
    911Python datatypes must be converted to column types in the DB. When writing 
    1012a StorageManager, you should make sure that your type conversions can handle 
    11 at least the following limitations. If possible, implement the type with no 
     13at least the following limitations: If possible, implement the type with no 
    1214limits. Also, follow UnitProperty.hints['bytes'] where possible. A value 
    1315of zero for hints['bytes'] implies no limit. If no value is given, try to 
     
    6163 
    6264 
    63 def getCoerceName(value, valuetype=None): 
     65def getCoerceName(valuetype): 
     66    """Return the name of the coercion method for a given type.""" 
    6467    mod = valuetype.__module__ 
    6568    if mod == "__builtin__": 
     
    7174 
    7275def getCoerceMethod(obj, value, valuetype=None): 
     76    """Return the coercion method for a given value [or type].""" 
    7377    if valuetype is None: 
    7478        valuetype = type(value) 
    7579     
    76     meth = getCoerceName(value, valuetype) 
     80    meth = getCoerceName(valuetype) 
    7781    if hasattr(obj, meth): 
    7882        return getattr(obj, meth) 
     
    8084    methods = [] 
    8185    for base in valuetype.__bases__: 
    82         meth = getCoerceName(value, base) 
     86        meth = getCoerceName(base) 
    8387        methods.append(meth) 
    8488        if hasattr(obj, meth): 
     
    322326    make decisions about coercion when they don't have control over 
    323327    the types of database columns, and must make do with legacy 
    324     databases implementations. 
     328    database implementations. 
    325329     
    326330    This base class is designed to work out-of-the-box with PostgreSQL 8. 
     
    430434 
    431435class TableRef: 
     436    """Wraps a table reference for use in SQLDecompiler's stack. 
     437     
     438    When we hit LOAD_FAST while decompiling, that should always be translated 
     439    into a table reference in the SQL. 
     440    """ 
    432441    def __init__(self, classname): 
    433442        self.classname = classname 
     
    724733 
    725734class ConnectionWrapper(object): 
     735    """Connection object wrapper, so it can be used as a weak reference.""" 
     736     
    726737    def __init__(self, conn=None): 
    727738        self.conn = conn 
     
    732743 
    733744class UnitClassWrapper(object): 
     745    """Unit class wrapper, for use in parsing multiselect joins.""" 
    734746     
    735747    def __init__(self, wclass, sm): 
     
    746758        # Place the identifier properties first 
    747759        # in case others depend upon them. 
    748         keys = list(wclass.identifiers) + [k for k in wclass.properties() 
     760        keys = list(wclass.identifiers) + [k for k in wclass.properties 
    749761                                           if k not in wclass.identifiers] 
    750762        cols = [(wclass, k) for k in keys] 
     
    778790 
    779791class StorageManagerDB(storage.StorageManager): 
    780     """StoreManager to save and retrieve Units using a DB.""" 
     792    """StoreManager base class to save and retrieve Units using a DB.""" 
    781793     
    782794    sql_name_max_length = 64 
     
    877889     
    878890    def connection(self): 
     891        """Return a connection from the pool.""" 
    879892        if not self.threaded: 
    880893            # Place a single 'conn' entry in self.refs. 
     
    932945     
    933946    def shutdown(self): 
     947        """Release all database connections.""" 
    934948        # Empty the pool. 
    935949        if self.pool: 
     
    946960     
    947961    def select(self, cls, expr, fields=None, distinct=False): 
     962        """Return an SQL SELECT statement, and an 'imperfect' flag. 
     963         
     964        imperfect: True or False depending on whether the generated SQL 
     965            perfectly satisfies the given expression. 
     966        """ 
    948967        clsname = cls.__name__ 
    949968        tablename = self.table_name(clsname) 
     
    967986     
    968987    def where(self, classnames, expr): 
     988        """Return an SQL WHERE clause, and an 'imperfect' flag. 
     989         
     990        imperfect: True or False depending on whether the generated SQL 
     991            perfectly satisfies the given expression. 
     992        """ 
    969993        decom = self.decompiler(classnames, expr, self, self.toAdapter) 
    970994        return decom.code(), decom.imperfect 
     
    9911015     
    9921016    def recall(self, cls, expr=None): 
     1017        """Yield a sequence of Unit instances which satisfy the expression.""" 
    9931018        clsname = cls.__name__ 
    9941019         
     
    10061031            props = [] 
    10071032            idnames = list(cls.identifiers) 
    1008             for key in idnames + [x for x in cls.properties() if x not in idnames]: 
     1033            for key in idnames + [x for x in cls.properties if x not in idnames]: 
    10091034                index, ftype = columns[self.column_name(clsname, key, quoted=False)] 
    10101035                subtype = self.expanded_columns.get((clsname, key)) 
     
    10781103        fields = [] 
    10791104        values = [] 
    1080         for key in cls.properties()
     1105        for key in cls.properties
    10811106            subtype = self.expanded_columns.get((clsname, key)) 
    10821107            if subtype: 
     
    10931118     
    10941119    def id_clause(self, unit): 
     1120        """Return an SQL expression for the identifiers of the given Unit.""" 
    10951121        clsname = unit.__class__.__name__ 
    10961122        col = self.column_name 
     
    11071133             
    11081134            parms = [] 
    1109             for key in cls.properties()
     1135            for key in cls.properties
    11101136                if key not in cls.identifiers: 
    11111137                    subtype = self.expanded_columns.get((clsname, key)) 
     
    12441270     
    12451271    def join(self, unitjoin): 
     1272        """Return an SQL FROM clause for the given unitjoin.""" 
    12461273        cls1, cls2 = unitjoin.class1, unitjoin.class2 
    12471274        if isinstance(cls1, dejavu.UnitJoin): 
     
    12791306     
    12801307    def multiselect(self, classes, expr): 
     1308        """Return an SQL SELECT statement, an imperfect flag, and column names.""" 
    12811309         
    12821310        # Create a new unitjoin tree where each class is wrapped. 
     
    13261354     
    13271355    def multirecall(self, classes, expr): 
    1328         """multirecall(classes, expr) -> Full inner join units.""" 
     1356        """Yield Unit instance sets which satisfy the expression.""" 
    13291357        sql, imp, supplied_cols = self.multiselect(classes, expr) 
    13301358        data, recvd_cols = self.fetch(sql) 
     
    13831411         
    13841412        fields = [] 
    1385         for key in cls.properties()
     1413        for key in cls.properties
    13861414            fields.append('%s %s' % (self.column_name(clsname, key), 
    13871415                                     typename(cls, key))) 
  • trunk/storage/sockets.py

    r190 r191  
    3131    else: 
    3232        # Assume it's a dejavu.Unit. 
    33         for key in unit.__class__.properties()
     33        for key in unit.properties
    3434            value = getattr(unit, key) 
    3535            if value.__class__.__name__ == "FixedPoint": 
     
    4646    except pickle.UnpicklingError: 
    4747        raise TypeError(data) 
    48     cls = unit.__class__ 
    49     for key in cls.properties(): 
     48    for key in unit.properties: 
    5049        # Set the attribute directly to avoid __set__ overhead. 
    5150        unit._properties[key] = attrdict[key] 
  • trunk/storage/storeado.py

    r190 r191  
    656656        # We have to delay expanded tables until we have an ID. 
    657657        to_expand = [] 
    658         for key in cls.properties()
     658        for key in cls.properties
    659659            typename = self.typeAdapter.coerce(cls, key) 
    660660            if "IDENTITY" in typename: 
     
    896896        # We have to delay expanded tables until we have an ID. 
    897897        to_expand = [] 
    898         for key in cls.properties()
     898        for key in cls.properties
    899899            typename = self.typeAdapter.coerce(cls, key) 
    900900            if typename.startswith("AUTOINCREMENT"): 
  • trunk/storage/storemysql.py

    r190 r191  
    233233        # We have to delay expanded tables until we have an ID. 
    234234        to_expand = [] 
    235         for key in cls.properties()
     235        for key in cls.properties
    236236            typename = self.typeAdapter.coerce(cls, key) 
    237237            if typename.endswith("AUTO_INCREMENT"): 
     
    281281        fields = [] 
    282282        pk = [] 
    283         for key in cls.properties()
     283        for key in cls.properties
    284284            qname = self.column_name(clsname, key) 
    285285            dbtype = typename(cls, key) 
  • trunk/storage/storepypgsql.py

    r190 r191  
    140140        # We have to delay expanded tables until we have an ID. 
    141141        to_expand = [] 
    142         for key in cls.properties()
     142        for key in cls.properties
    143143            typename = self.typeAdapter.coerce(cls, key) 
    144144            if 'nextval' in typename: 
     
    192192         
    193193        fields = [] 
    194         for key in cls.properties()
     194        for key in cls.properties
    195195            dbtype = typename(cls, key) 
    196196            if 'nextval' in dbtype: 
  • trunk/storage/storesqlite.py

    r190 r191  
    286286        # We have to delay expanded tables until we have an ID. 
    287287        to_expand = [] 
    288         for key in cls.properties()
     288        for key in cls.properties
    289289            if "AUTOINCREMENT" in self.typeAdapter.coerce(cls, key): 
    290290                # Skip this field, since we're using AUTOINCREMENT 
     
    329329        typename = self.typeAdapter.coerce 
    330330         
    331         for key in cls.properties()
     331        for key in cls.properties
    332332            if "AUTOINCREMENT" in typename(cls, key): 
    333333                # SQLite AUTOINCREMENT columns start at 1 by default. 
  • trunk/units.py

    r190 r191  
    381381            assocs = {} 
    382382         
    383         # Make a copy of the parent class' _properties keys, and store 
    384         # it in the _properties attribute of this subclass. In this 
     383        # Make a copy of the parent class' properties, and store 
     384        # it in the properties attribute of this subclass. In this 
    385385        # manner, Unit Property keys should propagate down to subclasses, 
    386386        # but not back up to superclasses. 
    387         if hasattr(cls, "_properties"): 
    388             props = dict.fromkeys(cls._properties.keys()) 
    389         else: 
    390             props = {} 
     387        if hasattr(cls, "properties"): 
     388            props = cls.properties[:] 
     389        else: 
     390            props = [] 
    391391         
    392392        for name, val in dct.iteritems(): 
     
    398398                if val.key is None: 
    399399                    val.key = name 
    400                 props[name] = val 
     400                if name not in props: 
     401                    props.append(name) 
    401402             
    402403            # Remove any properties from the parent class if requested 
    403404            # (request by binding the parent's UnitProperty.key to None). 
    404             if name in props and val is None
    405                 del props[name] 
    406                  
     405            if val is None and name in props
     406                props.remove(name) 
     407             
    407408            # Now grab any new UnitAssociations defined in this class. 
    408409            if isinstance(val, UnitAssociation): 
     
    410411                assocs[name] = val 
    411412         
    412         cls._properties = props 
     413        cls.properties = props 
    413414        cls._associations = assocs 
    414415         
     
    501502     
    502503    def __init__(self, **kwargs): 
    503         # Copy the class _properties dict into self._properties, 
     504        # Copy the class properties into self._properties, 
    504505        # setting each value to the UnitProperty.default. 
    505506        cls = self.__class__ 
    506507        self._properties = dict([(k, getattr(cls, k).default) 
    507                                  for k in cls._properties.keys()]) 
     508                                 for k in cls.properties]) 
    508509         
    509510        self.sandbox = None 
     
    525526    def __copy__(self): 
    526527        newUnit = self.__class__() 
    527         for key, prop in self.__class__._properties.iteritems()
     528        for key in self.properties
    528529            if key in self.identifiers: 
     530                prop = getattr(self.__class__, key) 
    529531                newUnit._properties[key] = prop.default 
    530532            else: 
     
    566568        """Set a Unit Property for cls.""" 
    567569        setattr(cls, key, descriptor(type, index, key=key)) 
    568         cls._properties[key] = None 
     570        if key not in cls.properties: 
     571            cls.properties.append(key) 
    569572    set_property = classmethod(set_property) 
    570573     
     
    577580    def remove_property(cls, key): 
    578581        delattr(cls, key) 
    579         del cls._properties[key] 
     582        cls.properties.remove(key) 
    580583    remove_property = classmethod(remove_property) 
    581584     
     
    583586        """cls.indices() -> tuple of names of indexed UnitProperties.""" 
    584587        product = [] 
    585         for key in cls.properties()
     588        for key in cls.properties
    586589            try: 
    587590                if getattr(cls, key).index: 
     
    592595        return tuple(product) 
    593596    indices = classmethod(indices) 
    594      
    595     def properties(cls): 
    596         """cls.properties() -> list of UnitProperty names.""" 
    597         return cls._properties.iterkeys() 
    598     properties = classmethod(properties) 
    599597     
    600598    def property_type(cls, key):