Changeset 33
- Timestamp:
- 11/21/04 03:30:23
- Files:
-
- trunk/__init__.py (modified) (8 diffs)
- trunk/doc/framework.html (modified) (2 diffs)
- trunk/engines.py (modified) (1 diff)
- trunk/readme.py (modified) (2 diffs)
- trunk/storage/__init__.py (modified) (2 diffs)
- trunk/storage/storeado.py (modified) (2 diffs)
- trunk/storage/storeodbc.py (modified) (3 diffs)
- trunk/storage/test_storeado.py (modified) (1 diff)
- trunk/test_dejavu.py (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/__init__.py
r32 r33 1 1 import ConfigParser 2 2 import datetime 3 3 import sha 4 try: 5 import cPickle as pickle 6 except ImportError: 7 import pickle 4 8 5 9 from dejavu.containers import * … … 134 138 if unit.sandbox and self.pre: 135 139 self.pre(unit, value) 136 unit.dirty = True137 140 unit._properties[self.key] = value 138 141 if unit.sandbox and self.post: … … 201 204 these processes should set the sandbox attribute. 202 205 203 dirty: a flag indicatingwhether elements in the _properties dictionary206 dirty: indicates whether elements in the _properties dictionary 204 207 have been modified. This flag is used by Sandboxes to optimize 205 208 forget(): they do not ask Storage Managers to save data for Units … … 228 231 229 232 self.sandbox = None 230 self.dirty = False231 233 232 234 for k, v in kwargs.iteritems(): 233 235 setattr(self, k, v) 236 self.cleanse() 237 238 def _property_hash(self): 239 return sha.new(pickle.dumps(self._properties)).digest() 240 241 def dirty(self): 242 return self._initial_property_hash != self._property_hash() 243 244 def cleanse(self): 245 self._initial_property_hash = self._property_hash() 234 246 235 247 def set_property(cls, key, type=unicode, index=False, … … 294 306 295 307 def __getstate__(self): 296 return (self._properties, self. dirty)308 return (self._properties, self._initial_property_hash) 297 309 298 310 def __setstate__(self, state): 299 311 self.sandbox = None 300 self._properties, self. dirty= state312 self._properties, self._initial_property_hash = state 301 313 302 314 def add(self, unit): … … 517 529 518 530 # Ask the store to accept the unit, assigning it an ID if 519 # necessary. The store should also set unit.dirty to False520 # if it saves the whole unit state on this call or not.531 # necessary. The store should also call unit.cleanse() 532 # if it saves the whole unit state on this call. 521 533 store = self.arena.storage(cls) 522 534 if store: … … 738 750 unit.on_repress() 739 751 740 if store and unit.dirty :752 if store and unit.dirty(): 741 753 store.save(unit) 742 754 … … 753 765 cls = unit.__class__ 754 766 store = self.arena.storage(cls) 755 if store and unit.dirty :767 if store and unit.dirty(): 756 768 store.save(unit) 757 769 trunk/doc/framework.html
r32 r33 150 150 which is a working decompiler for ADO databases. You should be able 151 151 to tweak it for most databases with a couple of SQL syntax changes. 152 [It does the decompilation in one pass right now, which is fragile 153 but works. I hope to move it to a two-pass version soon.]</li> 152 </li> 154 153 <li><tt>ADOSQLDecompiler</tt> is built on a simple Visitor-style base 155 class, <tt>codewalk.LambdaDecompiler</tt>. More complicated extensions156 are easily added to this base class; each bytecode in the Expression157 (Python lambda) gets its own method call.</li>154 class, <tt>codewalk.LambdaDecompiler</tt>. More complicated 155 extensions are easily added to this base class; each bytecode in 156 the Expression (Python lambda) gets its own method call.</li> 158 157 <li>You don't have to handle globals or cell references within the 159 158 lambda--when the lambda gets wrapped in an Expression, all free … … 189 188 in the sequence. You should probably lock this whole method in a 190 189 <tt>threading.Lock</tt>.</li> 191 <li><b>save</b>: If <tt>not unit.dirty </tt>, you can exit. Otherwise,190 <li><b>save</b>: If <tt>not unit.dirty()</tt>, you can exit. Otherwise, 192 191 iterate through the Unit's properties() and persist each value, 193 192 using an Adapter to coerce each value to the type expected by your 194 193 database (or not, it's up to you; storeado special-cases 195 UnitCollection, for example). Set <tt>unit.dirty = False</tt>.</li> 194 UnitCollection, for example). If you are able to persist all values, 195 call <tt>unit.cleanse()</tt> to mark the Unit as no longer dirty. 196 </li> 196 197 <li><b>destroy</b>: <tt>"DELETE * FROM [%(table)s] WHERE ID = 197 198 %(id)s"</tt>. That's all.</li> trunk/engines.py
r32 r33 56 56 57 57 def __getstate__(self): 58 return (self._properties, self. dirty)58 return (self._properties, self._initial_property_hash) 59 59 60 60 def __setstate__(self, state): 61 61 self.sandbox = None 62 62 self._mutex = threading.RLock() 63 self._properties, self. dirty= state63 self._properties, self._initial_property_hash = state 64 64 65 65 def acquire(self): trunk/readme.py
r32 r33 49 49 50 50 todo = """ 51 1. Batched triggers:52 Some pre/post triggers should be delayed until end of request,53 particularly those which affect related Units. Also on_forget(), etc.54 There should be a generic trigger mechanism with the ability for55 client code to register new trigger points. Probably each Unit class56 should maintain an attribute triggers = {}, where each key is a string57 and each value is a callable which gets passed "self". Standard58 triggers would include:59 1. Pre-modify property X60 2. Post-modify property X.61 3. Pre-forget/post-forget.62 4. on_flush (end of request)63 2. For UnitProperty types which are mutable (like dicts), we currently have to64 explicitly set dirty, because dirty only sets automatically on rebind,65 not mutate. Fix if possible, or doc the heck out of it.66 3. Speed issues:67 1. Chained, related Unit classes would be nice to have in Expressions.68 lambda x: x.Amount > 0 and x.Characteristic.Type = 'grr'69 2. It would be nice to declare e.g. Job has only one Project;70 this would allow job.Project() to set quota = 1 on recall.71 3. Batch memorize when adding several units with auto ID's.72 4. u"SELECT * FROM [djvMissionTrip] WHERE ((([djvMissionTrip].[LastDate] <> Null)73 and ([djvMissionTrip].[LastDate] >= #10/14/2004#))74 and ([djvMissionTrip].[Field] = 'BC'))75 and ([u'C'] Like '%djvMissionTrip].[Plan%')"76 5. More SM's.77 SQLite78 PostgreSQL79 shelve80 dbm81 82 83 51 2.0: 84 Classes are associated across Arenas. :/ But that shouldn't be a 52 1. Batched triggers: 53 Some pre/post triggers should be delayed until end of request, 54 particularly those which affect related Units. Also on_forget(), 55 etc. There should be a generic trigger mechanism with the ability 56 for client code to register new trigger points. Probably each Unit 57 class should maintain an attribute triggers = {}, where each key 58 is a string and each value is a callable which gets passed "self". 59 Standard triggers points would include: 60 1. Pre-modify property X 61 2. Post-modify property X. 62 3. Pre-forget/post-forget. 63 4. on_flush (end of request) 64 2. Speed issues: 65 1. Chained, related Unit classes would be nice to have in Expressions. 66 lambda x: x.Amount > 0 and x.Characteristic.Type = 'grr' 67 2. It would be nice to declare e.g. Job has only one Project; 68 this would allow job.Project() to set quota = 1 on recall. 69 3. Batch memorize when adding several units with auto ID's. 70 3. More SM's. 71 SQLite 72 PostgreSQL 73 MySQL 74 shelve 75 dbm 76 4. Classes are associated across Arenas. :/ But that shouldn't be a 85 77 huge issue--it only affects those who wish to deploy the same app 86 78 twice within the same process--an odd corner case. 87 Add the new (python 2.3) Decimal type.88 UnitProperty.pre should be able to modify value.89 http://sqlobject.org/docs/FAQ.html would be a great page to rewrite79 5. Add the new (python 2.3) Decimal type. 80 6. UnitProperty.pre should be able to modify value. 81 7. http://sqlobject.org/docs/FAQ.html would be a great page to rewrite 90 82 with respect to dejavu instead of sqlobject. 91 83 """ … … 98 90 2. UnitProperty now supplies .key from local attribute name when 99 91 specified in class body (see MetaUnit __init__). 92 3. Made Decompiler less fragile by adding stack sentinels: 93 cannot_represent, table_arg, kw_arg 94 4. Rewrote handling of Unit.dirty. For UnitProperty types which were 95 mutable (like list and dict), we had to explicitly set dirty, 96 because dirty only set automatically on rebind, not mutate. 97 New system uses a hash of unit._properties; unit.dirty() compares 98 unit._initial_property_hash with the current hash, returning True 99 if they differ. Unit.cleanse() sets _initial_property_hash to the 100 current hash, and is called in Unit.__init__(). 100 101 101 102 1.2.5 (11/15/04): trunk/storage/__init__.py
r32 r33 159 159 """Store the unit.""" 160 160 # Defer store.save() for sweepers. 161 if unit.dirty :161 if unit.dirty(): 162 162 lock = self._get_lock(unit.__class__) 163 163 try: … … 205 205 or lastRecall < lastSweepTime): 206 206 unit = pickle.loads(cache[id]) 207 if unit.dirty :207 if unit.dirty(): 208 208 self.nextstore.save(unit) 209 209 trunk/storage/storeado.py
r32 r33 855 855 The ID should be supplied by UnitSequencers via reserve(). 856 856 """ 857 if unit.dirty or forceSave:857 if unit.dirty() or forceSave: 858 858 cls = unit.__class__ 859 859 clsname = cls.__name__ … … 884 884 anRS.Update() 885 885 anRS.Close() 886 unit. dirty = False886 unit.cleanse() 887 887 888 888 def save_expanded(self, unit, key): trunk/storage/storeodbc.py
r32 r33 278 278 coercer.consume(eachKey, row[self.colIndices[eachKey.lower()]]) 279 279 unit.concrete = True 280 unit. dirty = False280 unit.cleanse() 281 281 return True 282 282 … … 423 423 The ID should be already supplied by the UnitServer(s). 424 424 """ 425 if unit.dirty or forceSave:425 if unit.dirty() or forceSave: 426 426 # Use an UPDATE command. 427 427 SETAtoms = [u"%s = %s" % (eachKey, savecoercer.coerce(getattr(unit, eachKey))) … … 449 449 % (tablename, unit.ID)) 450 450 self.execute(insertStatement) 451 unit. dirty = False451 unit.cleanse() 452 452 return True 453 453 trunk/storage/test_storeado.py
r32 r33 191 191 # This broke on 5/10/04, because "== None" wasn't succeeding as "= Null". 192 192 trial(lambda x: x.DateTo == None, u"[djvThings].[DateTo] Is Null", False) 193 193 194 # Another one that broke sometime in 2004. Rev 32 seems to have fixed it. 195 trial(lambda x: 'C' in x.Plan, "[djvThings].[Plan] Like '%C%'", True) 196 194 197 # Multiple arguments (? Why should this be supported?) 195 198 trial(lambda x, y, z: x.Date == 3 and y.Qty > 4 and z.Qty < 20, trunk/test_dejavu.py
r32 r33 13 13 a.ID = 321 14 14 self.assertEqual(a.ID, 321) 15 self.assertEqual(a.dirty , True)15 self.assertEqual(a.dirty(), True) 16 16 a.ID = '444' 17 17 self.assertEqual(a.ID, 444) … … 33 33 Unicode = dejavu.UnitProperty(unicode) 34 34 Datetime = Triggered(datetime.datetime) 35 Dict = dejavu.UnitProperty(dict) 35 36 36 37 # Instance creation and population 37 38 t = Thing(Integer=3, String='abc', Unicode=u'foo') 39 self.assertEqual(t.dirty(), False) 38 40 self.assertEqual(t.Integer, 3) 39 41 self.assertEqual(t.String, 'abc') … … 42 44 self.assertEqual(type(t.Unicode), unicode) 43 45 self.assertEqual(t.Datetime, None) 46 self.assertEqual(t.Dict, None) 44 47 45 48 # Index attribute 46 49 self.assertEqual(t.__class__.Integer.index, True) 47 50 48 # Triggers. Unit needs a sandbox or pre ,post won't be called.51 # Triggers. Unit needs a sandbox or pre/post won't be called. 49 52 t.sandbox = True 50 53 self.assertEqual(hasattr(t, 'preval'), False) … … 52 55 self.assertEqual(t.preval, aDate) 53 56 self.assertEqual(t.postval, aDate) 54 57 self.assertEqual(t.dirty(), True) 58 59 t.cleanse() 60 t.Dict = aDict = {'k': 'blah'} 61 self.assertEqual(t.dirty(), True) 62 self.assertEqual(t.Dict, aDict) 55 63 56 64 if __name__ == "__main__":
