Contact: fumanchu@aminus.org

Log in as guest/dejavu to create tickets

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

Changeset 21

Show
Ignore:
Timestamp:
10/20/04 21:36:52
Author:
fumanchu
Message:

1. Removed Unit.temporary
2. Added Unit.on_recall() with Sandbox support.
3. Engines etc replaced temporary with Expiration, permanent() with immortalize().

Files:

Legend:

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

    r20 r21  
    110110    post = None 
    111111     
    112     def __init__(self, key, type, index=False, hints={}): 
     112    def __init__(self, key, type=unicode, index=False, hints={}): 
    113113        self.key = key 
    114114        self.type = type 
     
    201201        which have not been modified. Because SM's may cache Units, no code 
    202202        should set this flag other than UnitProperty.__set__ and SM's. 
    203      
    204     temporary: a flag indicating that the Unit should NOT be saved to 
    205         permanent storage. This should be False for most Units; some Units 
    206         may require an additional condition (usually a confirmation by the 
    207         user) that their state should persist. Note that the implementation 
    208         of 'temporary' is left to Storage Managers, not Sandboxes. 
    209203    """ 
    210204     
     
    220214        self.sandbox = None 
    221215        self.dirty = False 
    222         self.temporary = False 
    223216         
    224217        for k, v in kwargs.iteritems(): 
     
    286279     
    287280    def __getstate__(self): 
    288         return (self._properties, self.dirty, self.temporary
     281        return (self._properties, self.dirty
    289282     
    290283    def __setstate__(self, state): 
    291284        self.sandbox = None 
    292         self._properties, self.dirty, self.temporary = state 
     285        self._properties, self.dirty = state 
    293286     
    294287    def add(self, unit): 
     
    597590                pairs.append((c, e)) 
    598591             
    599             # Give up on in-memory techniques, flush it all, 
     592            # Don't try any in-memory techniques, just flush it all, 
    600593            # and ask the SM to give us what we want. 
    601594            self.flush(cls) 
     
    603596                self.flush(c) 
    604597            for units in store.recall(cls, expr, pairs): 
     598                confirmed = True 
    605599                for unit in units: 
    606600                    if unit is not None: 
     
    610604                            cache[ID] = unit 
    611605                        unit.sandbox = self 
    612                 yield units 
     606                    if hasattr(unit, 'on_recall'): 
     607                        try: 
     608                            unit.on_recall() 
     609                        except UnrecallableError: 
     610                            confirmed = False 
     611                if confirmed: 
     612                    yield units 
    613613            raise StopIteration 
    614614         
     
    626626                expectedID = fc.co_consts[-1] 
    627627                if cache.has_key(expectedID): 
    628                     yield cache[expectedID] 
     628                    unit = cache[expectedID] 
     629                    # Do NOT call on_recall here. That should be called 
     630                    # only at the Sandbox-SM boundary. 
     631                    yield unit 
    629632                    raise StopIteration 
    630633                else: 
     
    634637            for unit in cache.itervalues(): 
    635638                if expr is None or expr.evaluate(unit): 
     639                    # Do NOT call on_recall here. That should be called 
     640                    # only at the Sandbox-SM boundary. 
    636641                    yield unit 
    637642         
     
    645650                    cache[ID] = unit 
    646651                    unit.sandbox = self 
    647                     yield unit 
     652                    confirmed = True 
     653                    if hasattr(unit, 'on_recall'): 
     654                        try: 
     655                            unit.on_recall() 
     656                        except UnrecallableError: 
     657                            confirmed = False 
     658                    if confirmed: 
     659                        yield unit 
    648660     
    649661    def unit(self, cls, **kwargs): 
     
    763775    pass 
    764776 
    765 class TemporaryUnitError(DejavuError): 
    766     """Exception raised when a temporary Unit was sought but not found.""" 
     777class UnrecallableError(DejavuError): 
     778    """Exception raised when a Unit was sought but not recalled.""" 
    767779    pass 
    768780 
  • trunk/doc/modeling.html

    r20 r21  
    221221accomplish this.</p> 
    222222 
     223<h5>recall()</h5> 
    223224<p>First, the appropriately named <tt>recall(cls, expr)</tt> function. 
    224225This is the full-blown query method. As a first argument, you pass it the 
     
    245246and are related.</p> 
    246247 
     248<p>If your Unit class defines an <tt>on_recall()</tt> method, it will be 
     249called when each Unit has been loaded from storage (at the end of the 
     250recall process). Once the unit is loaded into a Sandbox, however, 
     251<tt>on_recall</tt> will not be called; it's only called at the Sandbox/SM 
     252boundary. If <tt>on_recall</tt> raises <tt>UnrecallableError</tt>, the 
     253unit will not be yielded back to the caller, nor placed in the Sandbox 
     254cache.</p> 
     255 
     256<h5>unit()</h5> 
    247257<p>The <tt>recall</tt> method can be verbose. When you want a one-liner 
    248258and only expect a single Unit, use the <tt>unit(cls, **kw)</tt> method 
     
    284294the trick. Notice that flushing calls <tt>repress()</tt> for each Unit in 
    285295the Sandbox, and any <tt>on_repress()</tt> triggers will be executed.</p> 
    286  
    287 <p>Some Units may have their <tt>temporary</tt> flag set. When you use 
    288 a <tt>CachingProxy</tt> as a Storage Manager, temporary Units will be 
    289 destroyed rather than persisted. This happens independently of forgetting 
    290 and sandbox flushing.</p> 
    291296 
    292297 
  • trunk/engines.py

    r20 r21  
    22Notice in particular that UnitCollection, UnitEngineRule, and UnitEngine 
    33are all _temporary_ Units. Even when you memorize them, they won't be 
    4 persistent unless you mark each instance as no longer temporary. If you 
    5 use UnitEngine.permanent(), it will make all of its rules permanent 
    6 (not temporary) as well. 
     4persistent unless you set each instance's Expiration to None. If you 
     5use UnitEngine.immortalize(), it will make all of its rules immortal 
     6(no Expiration) as well. 
    77""" 
    88 
     
    4242     
    4343    _IDs = None 
     44    EngineID = dejavu.UnitProperty(u'EngineID', int, index=True) 
     45    Type = dejavu.UnitProperty(u'Type') 
     46    Expiration = dejavu.UnitProperty(u'Expiration', datetime.datetime) 
     47    Timestamp = dejavu.UnitProperty(u'Timestamp', datetime.datetime) 
    4448     
    4549    def __init__(self, **kwargs): 
     
    5256     
    5357    def __getstate__(self): 
    54         return (self._properties, self.dirty, self.temporary, self._IDs) 
     58        return (self._properties, self.dirty, self._IDs) 
    5559     
    5660    def __setstate__(self, state): 
    5761        self.sandbox = None 
    5862        self._mutex = threading.RLock() 
    59         self._properties, self.dirty, self.temporary, self._IDs = state 
     63        self._properties, self.dirty, self._IDs = state 
    6064     
    6165    def acquire(self): 
     
    116120        newUnit._IDs = self._IDs.copy() 
    117121        return newUnit 
    118  
    119 UnitCollection.set_property(u'EngineID', int, index=True) 
    120 UnitCollection.set_properties({u'Type': unicode, 
    121                                u'Timestamp': datetime.datetime, 
    122                                }) 
     122     
     123    def on_recall(self): 
     124        if self.Expiration is not None: 
     125            if self.Expiration <= datetime.datetime.now(): 
     126                self.forget() 
     127                raise dejavu.UnrecallableError 
     128            else: 
     129                self.decay(minutes=15) 
     130     
     131    def decay(self, **kw): 
     132        """decay(**kw) -> Set Expiration to now() + timedelta(**kw).""" 
     133        self.Expiration = datetime.datetime.now() + datetime.timedelta(**kw) 
    123134 
    124135 
     
    136147 
    137148 
     149class RuleProperty(dejavu.UnitProperty): 
     150    def post(self, unit, value): 
     151        eng = unit.sandbox.unit(UnitEngine, ID=unit.EngineID) 
     152        if eng: 
     153            eng.update_final_class() 
     154 
    138155class UnitEngineRule(dejavu.Unit): 
    139156    """A Rule for Unit Engines.""" 
     157     
     158    Operation = RuleProperty(u'Operation', str) 
     159    SetID = RuleProperty(u'SetID', int) 
     160    Operand = RuleProperty(u'Operand', str, False, hints = {u'Size': 0}) 
     161    Sequence = RuleProperty(u'Sequence', int) 
     162    EngineID = dejavu.UnitProperty(u'EngineID', int, index=True) 
     163    Expiration = dejavu.UnitProperty(u'Expiration', datetime.datetime) 
    140164     
    141165    def __init__(self, **kwargs): 
     
    193217            return pickle.loads(op) 
    194218        return None 
    195  
    196 class RuleProperty(dejavu.UnitProperty): 
    197     def post(self, unit, value)
    198         eng = unit.sandbox.unit(UnitEngine, ID=unit.EngineID) 
    199         if eng: 
    200             eng.update_final_class() 
    201 UnitEngineRule.set_property(u'Operation', str, descriptor=RuleProperty) 
    202 UnitEngineRule.set_property(u'SetID', int, descriptor=RuleProperty
    203 UnitEngineRule.set_property(u'Operand', str, descriptor=RuleProperty) 
    204 UnitEngineRule.Operand.hints = {u'Size': 0} 
    205 UnitEngineRule.set_property(u'Sequence', int, descriptor=RuleProperty) 
    206 UnitEngineRule.set_property(u'EngineID', int, index=True
     219     
     220    def on_recall(self): 
     221        if self.Expiration is not None
     222            if self.Expiration <= datetime.datetime.now(): 
     223                self.forget() 
     224                raise dejavu.UnrecallableError 
     225            else: 
     226                self.decay(minutes=15
     227     
     228    def decay(self, **kw): 
     229        """decay(**kw) -> Set Expiration to now() + timedelta(**kw).""" 
     230        self.Expiration = datetime.datetime.now() + datetime.timedelta(**kw
    207231 
    208232 
    209233class UnitEngine(dejavu.Unit): 
    210234    """A factory for Unit Collections.""" 
     235     
     236    Owner = dejavu.UnitProperty(u'Owner') 
     237    Name = dejavu.UnitProperty(u'Name') 
     238    Created = dejavu.UnitProperty(u'Created', datetime.datetime) 
     239    FinalClassName = dejavu.UnitProperty(u'FinalClassName') 
     240    Expiration = dejavu.UnitProperty('Expiration', datetime.datetime) 
    211241     
    212242    def __init__(self, **kwargs): 
     
    225255        for snap in self.snapshots(): 
    226256            snap.forget() 
     257     
     258    def on_recall(self): 
     259        if self.Expiration is not None: 
     260            if self.Expiration <= datetime.datetime.now(): 
     261                msg = ("Forgetting Engine %s. Exp = %s. Now = %s" % 
     262                       (self.ID, self.Expiration, datetime.datetime.now())) 
     263                self.sandbox.arena.application.logger.info(msg) 
     264                self.forget() 
     265                raise dejavu.UnrecallableError 
     266            else: 
     267                self.decay(minutes=15) 
     268     
     269    def decay(self, **kw): 
     270        """decay(**kw) -> Set Expiration to now() + timedelta(**kw).""" 
     271        self.Expiration = datetime.datetime.now() + datetime.timedelta(**kw) 
    227272     
    228273    def update_final_class(self): 
     
    269314         
    270315        newRule.EngineID = self.ID 
    271         newRule.temporary = self.temporary 
     316        newRule.Expiration = self.Expiration 
    272317        self.sandbox.memorize(newRule) 
    273318        self.update_final_class() 
     
    286331        if snap is not None: 
    287332            snap.EngineID = self.ID 
    288             snap.Timestamp = datetime.datetime.now() 
    289             snap.temporary = True 
     333            now = datetime.datetime.now() 
     334            snap.Timestamp = now 
     335            snap.decay(minutes=15) 
    290336            self.sandbox.memorize(snap) 
    291337        return snap 
     
    299345        return aSnap 
    300346     
    301     def permanent(self): 
    302         self.temporary = Fals
     347    def immortalize(self): 
     348        self.Expiration = Non
    303349        for rule in self.rules(): 
    304             rule.temporary = Fals
     350            rule.Expiration = Non
    305351     
    306352    def __copy__(self): 
     
    314360        newUnit = self.__copy__() 
    315361        newUnit.Owner = user 
    316         newUnit.temporary = temporary 
     362        if temporary: 
     363            newUnit.decay(minutes=15) 
     364        else: 
     365            newUnit.Expiration = None 
    317366        self.sandbox.memorize(newUnit) 
    318367        for rule in self.rules(): 
    319368            newRule = rule.__copy__() 
    320369            newRule.EngineID = newUnit.ID 
    321             newRule.temporary = temporary 
     370            newRule.Expiration = newUnit.Expiration 
    322371            self.sandbox.memorize(newRule) 
    323372        return newUnit 
     
    328377        else: 
    329378            return self.Owner in ('System', 'Public', user) 
    330  
    331 UnitEngine.set_properties({u'Owner': unicode, 
    332                            u'Name': unicode, 
    333                            u'Created': datetime.datetime, 
    334                            u'FinalClassName': unicode, 
    335                            }) 
    336379 
    337380 
     
    502545            B.release() 
    503546 
    504  
  • trunk/readme.py

    r20 r21  
    7171    and ([djvMissionTrip].[Field] = 'BC')) 
    7272    and ([u'C'] Like '%djvMissionTrip].[Plan%')" 
     735. Investigate making UnitCollection._IDs a full property (a list). This 
     74    would make pickling/unpickling easier. 
     756. More SM's. 
     76    SQLite 
     77    PostgreSQL 
     78    shelve 
     79    dbm 
    7380 
    7481 
     
    95102        Works directly on objects now. 
    96103    6. Moved codewalk and logic modules into dejavu. 
     104    7. Major bug in CachingProxy with persistent Unit.sandbox 
     105        attributes. Fix is to pickle each Unit instead of caching 
     106        them directly. 
     107    8. Added __getstate__ and __setstate__ to Unit and subclasses. 
    97108 
    981091.2.4 (10/4/04): 
  • trunk/storage/__init__.py

    r20 r21  
    119119                in self._caches.get(cls, {}).itervalues()] 
    120120     
    121     def recall(self, unitClass, expr=None): 
    122         """Return a Unit iterator.""" 
     121    def _get_lock(self, unitClass): 
    123122        if unitClass not in self._caches: 
    124123            self._caches[unitClass] = {} 
    125124            self._cache_locks[unitClass] = thread.allocate_lock() 
    126125        lock = self._cache_locks[unitClass] 
    127          
     126        lock.acquire(True) 
     127        return lock 
     128     
     129    def recall(self, unitClass, expr=None): 
     130        """Return a Unit iterator.""" 
    128131        currentTime = datetime.datetime.now() 
    129         lock.acquire(True
     132        lock = self._get_lock(unitClass
    130133        try: 
    131134            cache = self._caches[unitClass] 
     
    161164        """Delete the unit.""" 
    162165        unitClass = unit.__class__ 
    163         if unitClass not in self._caches: 
    164             self._caches[unitClass] = {} 
    165             self._cache_locks[unitClass] = thread.allocate_lock() 
    166         lock = self._cache_locks[unitClass] 
    167          
    168         lock.acquire(True) 
     166        lock = self._get_lock(unitClass) 
    169167        try: 
    170168            cache = self._caches[unitClass] 
     
    180178        """Reserve storage space for the Unit.""" 
    181179        unitClass = unit.__class__ 
    182         if unitClass not in self._caches: 
    183             self._caches[unitClass] = {} 
    184             self._cache_locks[unitClass] = thread.allocate_lock() 
    185         lock = self._cache_locks[unitClass] 
    186          
    187         lock.acquire(True) 
     180        lock = self._get_lock(unitClass) 
    188181        try: 
    189182            cache = self._caches[unitClass] 
     
    197190            lock.release() 
    198191     
    199     def sweep(self, cls, lastSweepTime=None): 
    200         if cls not in self._caches: 
    201             self._caches[cls] = {} 
    202             self._cache_locks[cls] = thread.allocate_lock() 
    203         cache = self._caches[cls] 
    204         lock = self._cache_locks[cls] 
    205         lock.acquire(True) 
    206         try: 
     192    def sweep(self, unitClass, lastSweepTime=None): 
     193        lock = self._get_lock(unitClass) 
     194        try: 
     195            cache = self._caches[unitClass] 
    207196            for id in cache.keys(): 
    208197                lastRecall = self._recallTimes.setdefault(id, None) 
     
    210199                    or lastRecall < lastSweepTime): 
    211200                    unit = pickle.loads(cache[id]) 
    212                     if unit.temporary: 
    213                         self.nextstore.destroy(unit) 
    214                     elif unit.dirty: 
     201                    if unit.dirty: 
    215202                        self.nextstore.save(unit) 
    216203                     
     
    250237    def recall(self, unitClass, expr=None): 
    251238        """Return a Unit iterator.""" 
    252         if unitClass not in self._caches: 
    253             self._caches[unitClass] = {} 
    254             self._cache_locks[unitClass] = thread.allocate_lock() 
    255         lock = self._cache_locks[unitClass] 
    256          
    257         lock.acquire(True) 
     239        lock = self._get_lock(unitClass) 
    258240        try: 
    259241            cache = self._caches[unitClass]