Changeset 21
- Timestamp:
- 10/20/04 21:36:52
- Files:
-
- trunk/__init__.py (modified) (11 diffs)
- trunk/doc/modeling.html (modified) (3 diffs)
- trunk/engines.py (modified) (13 diffs)
- trunk/readme.py (modified) (2 diffs)
- trunk/storage/__init__.py (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/__init__.py
r20 r21 110 110 post = None 111 111 112 def __init__(self, key, type , index=False, hints={}):112 def __init__(self, key, type=unicode, index=False, hints={}): 113 113 self.key = key 114 114 self.type = type … … 201 201 which have not been modified. Because SM's may cache Units, no code 202 202 should set this flag other than UnitProperty.__set__ and SM's. 203 204 temporary: a flag indicating that the Unit should NOT be saved to205 permanent storage. This should be False for most Units; some Units206 may require an additional condition (usually a confirmation by the207 user) that their state should persist. Note that the implementation208 of 'temporary' is left to Storage Managers, not Sandboxes.209 203 """ 210 204 … … 220 214 self.sandbox = None 221 215 self.dirty = False 222 self.temporary = False223 216 224 217 for k, v in kwargs.iteritems(): … … 286 279 287 280 def __getstate__(self): 288 return (self._properties, self.dirty , self.temporary)281 return (self._properties, self.dirty) 289 282 290 283 def __setstate__(self, state): 291 284 self.sandbox = None 292 self._properties, self.dirty , self.temporary= state285 self._properties, self.dirty = state 293 286 294 287 def add(self, unit): … … 597 590 pairs.append((c, e)) 598 591 599 # Give up on in-memory techniques,flush it all,592 # Don't try any in-memory techniques, just flush it all, 600 593 # and ask the SM to give us what we want. 601 594 self.flush(cls) … … 603 596 self.flush(c) 604 597 for units in store.recall(cls, expr, pairs): 598 confirmed = True 605 599 for unit in units: 606 600 if unit is not None: … … 610 604 cache[ID] = unit 611 605 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 613 613 raise StopIteration 614 614 … … 626 626 expectedID = fc.co_consts[-1] 627 627 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 629 632 raise StopIteration 630 633 else: … … 634 637 for unit in cache.itervalues(): 635 638 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. 636 641 yield unit 637 642 … … 645 650 cache[ID] = unit 646 651 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 648 660 649 661 def unit(self, cls, **kwargs): … … 763 775 pass 764 776 765 class TemporaryUnitError(DejavuError):766 """Exception raised when a temporary Unit was sought but not found."""777 class UnrecallableError(DejavuError): 778 """Exception raised when a Unit was sought but not recalled.""" 767 779 pass 768 780 trunk/doc/modeling.html
r20 r21 221 221 accomplish this.</p> 222 222 223 <h5>recall()</h5> 223 224 <p>First, the appropriately named <tt>recall(cls, expr)</tt> function. 224 225 This is the full-blown query method. As a first argument, you pass it the … … 245 246 and are related.</p> 246 247 248 <p>If your Unit class defines an <tt>on_recall()</tt> method, it will be 249 called when each Unit has been loaded from storage (at the end of the 250 recall 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 252 boundary. If <tt>on_recall</tt> raises <tt>UnrecallableError</tt>, the 253 unit will not be yielded back to the caller, nor placed in the Sandbox 254 cache.</p> 255 256 <h5>unit()</h5> 247 257 <p>The <tt>recall</tt> method can be verbose. When you want a one-liner 248 258 and only expect a single Unit, use the <tt>unit(cls, **kw)</tt> method … … 284 294 the trick. Notice that flushing calls <tt>repress()</tt> for each Unit in 285 295 the 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 use288 a <tt>CachingProxy</tt> as a Storage Manager, temporary Units will be289 destroyed rather than persisted. This happens independently of forgetting290 and sandbox flushing.</p>291 296 292 297 trunk/engines.py
r20 r21 2 2 Notice in particular that UnitCollection, UnitEngineRule, and UnitEngine 3 3 are all _temporary_ Units. Even when you memorize them, they won't be 4 persistent unless you mark each instance as no longer temporary. If you5 use UnitEngine. permanent(), it will make all of its rules permanent6 (no t temporary) as well.4 persistent unless you set each instance's Expiration to None. If you 5 use UnitEngine.immortalize(), it will make all of its rules immortal 6 (no Expiration) as well. 7 7 """ 8 8 … … 42 42 43 43 _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) 44 48 45 49 def __init__(self, **kwargs): … … 52 56 53 57 def __getstate__(self): 54 return (self._properties, self.dirty, self. temporary, self._IDs)58 return (self._properties, self.dirty, self._IDs) 55 59 56 60 def __setstate__(self, state): 57 61 self.sandbox = None 58 62 self._mutex = threading.RLock() 59 self._properties, self.dirty, self. temporary, self._IDs = state63 self._properties, self.dirty, self._IDs = state 60 64 61 65 def acquire(self): … … 116 120 newUnit._IDs = self._IDs.copy() 117 121 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) 123 134 124 135 … … 136 147 137 148 149 class 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 138 155 class UnitEngineRule(dejavu.Unit): 139 156 """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) 140 164 141 165 def __init__(self, **kwargs): … … 193 217 return pickle.loads(op) 194 218 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) 207 231 208 232 209 233 class UnitEngine(dejavu.Unit): 210 234 """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) 211 241 212 242 def __init__(self, **kwargs): … … 225 255 for snap in self.snapshots(): 226 256 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) 227 272 228 273 def update_final_class(self): … … 269 314 270 315 newRule.EngineID = self.ID 271 newRule. temporary = self.temporary316 newRule.Expiration = self.Expiration 272 317 self.sandbox.memorize(newRule) 273 318 self.update_final_class() … … 286 331 if snap is not None: 287 332 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) 290 336 self.sandbox.memorize(snap) 291 337 return snap … … 299 345 return aSnap 300 346 301 def permanent(self):302 self. temporary = False347 def immortalize(self): 348 self.Expiration = None 303 349 for rule in self.rules(): 304 rule. temporary = False350 rule.Expiration = None 305 351 306 352 def __copy__(self): … … 314 360 newUnit = self.__copy__() 315 361 newUnit.Owner = user 316 newUnit.temporary = temporary 362 if temporary: 363 newUnit.decay(minutes=15) 364 else: 365 newUnit.Expiration = None 317 366 self.sandbox.memorize(newUnit) 318 367 for rule in self.rules(): 319 368 newRule = rule.__copy__() 320 369 newRule.EngineID = newUnit.ID 321 newRule. temporary = temporary370 newRule.Expiration = newUnit.Expiration 322 371 self.sandbox.memorize(newRule) 323 372 return newUnit … … 328 377 else: 329 378 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 })336 379 337 380 … … 502 545 B.release() 503 546 504 trunk/readme.py
r20 r21 71 71 and ([djvMissionTrip].[Field] = 'BC')) 72 72 and ([u'C'] Like '%djvMissionTrip].[Plan%')" 73 5. Investigate making UnitCollection._IDs a full property (a list). This 74 would make pickling/unpickling easier. 75 6. More SM's. 76 SQLite 77 PostgreSQL 78 shelve 79 dbm 73 80 74 81 … … 95 102 Works directly on objects now. 96 103 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. 97 108 98 109 1.2.4 (10/4/04): trunk/storage/__init__.py
r20 r21 119 119 in self._caches.get(cls, {}).itervalues()] 120 120 121 def recall(self, unitClass, expr=None): 122 """Return a Unit iterator.""" 121 def _get_lock(self, unitClass): 123 122 if unitClass not in self._caches: 124 123 self._caches[unitClass] = {} 125 124 self._cache_locks[unitClass] = thread.allocate_lock() 126 125 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.""" 128 131 currentTime = datetime.datetime.now() 129 lock .acquire(True)132 lock = self._get_lock(unitClass) 130 133 try: 131 134 cache = self._caches[unitClass] … … 161 164 """Delete the unit.""" 162 165 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) 169 167 try: 170 168 cache = self._caches[unitClass] … … 180 178 """Reserve storage space for the Unit.""" 181 179 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) 188 181 try: 189 182 cache = self._caches[unitClass] … … 197 190 lock.release() 198 191 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] 207 196 for id in cache.keys(): 208 197 lastRecall = self._recallTimes.setdefault(id, None) … … 210 199 or lastRecall < lastSweepTime): 211 200 unit = pickle.loads(cache[id]) 212 if unit.temporary: 213 self.nextstore.destroy(unit) 214 elif unit.dirty: 201 if unit.dirty: 215 202 self.nextstore.save(unit) 216 203 … … 250 237 def recall(self, unitClass, expr=None): 251 238 """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) 258 240 try: 259 241 cache = self._caches[unitClass]
