Re: Patch: HistoricalRevisions, atct_history support for annotation storage (used by newer ATCT)
Matt Hahnfeld <[email protected]>
| Newsgroups | gmane.comp.web.zope.plone.archetypes.devel |
|---|---|
| Message-ID | <[email protected]> |
> On Thu, 09 Mar 2006 20:41:52 +0000, Martin Aspeli wrote: > >>> If this is going to go into Zope, which I think it should, we need to >>> find a more generic way to handle the issue. The current patch is short >>> and simple, but it only handles __annotation__ attributes. It provides a >>> very simple fix for the issue at hand (and actually works very well for >>> our needs!), but probably isn't something the Zope folks would want to >>> add >>> to Zope. >> >> I see the problem. Perhaps we can replicate some of this code at the AT >> storage level and not have to use OFS.* directly at all instead? Below is a proof of concept for a generic low-level solution. I implemented it as its own separate class, but it could probably subclass the ZODB serializer, since it's almost entirely copied from there. In my testing, it works for ATDocuments (using annotation storage) on a clean Plone 2.1.2/Zope 2.8.5 site using "zopectl debug". I haven't had time to test much beyond that (feedback welcome!). It should work for other AT field storages that use subobjects too (are there any?). Because of the way this is implemented, something like this could probably be a part of the AT or ATCT layer -- if it wasn't included in Zope. As you can see, this class essentially just overrides the Unpickler's persistent_load to provide old versions of subobjects (err... references to other objects) when they are loaded from the ZODB. I also removed all caching functionality, since you wouldn't want to use the cache when you're getting a historical revision. I'll work on cleaning this up over the next couple of days. Feedback? Matt Hahnfeld [email protected] --- import OFS.History import ZODB.broken import cPickle import cStringIO # TimeTraveler # ZODB (deep) historical objects -- proof of concept # Matt Hahnfeld 3/10/06 # # Most of this code was stolen from ZODB/Connection.py # or ZODB/serialize.py. This should probably subclass # ZODB.Serialize.ObjectReader. # # Usage: # # get tid from p._p_jar._storage.history(p._p_oid, size=10) # # from TimeTraveler import TimeTraveler # p = app.my_plone_site.my_page # tt = TimeTraveler(p,'\x03c\xff\xcd\x15X\x80\xcc') # p_old = tt.get() # # p_old will be a deep copy of p for the tid specified. class TimeTraveler: def __init__(self, obj, tid): self._obj = obj self._tid = tid self._conn = self._obj._p_jar self._storage = self._conn._storage self._factory = self._conn._db.classFactory def get(self): obj = self._get_object(self._obj._p_oid) return obj.__of__(self._obj.aq_parent) def _get_object(self, oid): print 'in getobj' pickle = self._storage.loadSerial(oid, self._tid) unpickler = self._get_unpickler(pickle) klass = unpickler.load() if isinstance(klass, tuple): # Here we have a separate class and args. # This could be an old record, so the class module ne a named # refernce klass, args = klass print 'klass is '+str(klass) if isinstance(klass, tuple): # Old module_name, class_name tuple klass = self._get_class(*klass) if args is None: args = () else: # Definitely new style direct class reference args = () if issubclass(klass, ZODB.broken.Broken): # We got a broken class. We might need to make it # PersistentBroken if not issubclass(klass, ZODB.broken.PersistentBroken): klass = ZODB.broken.persistentBroken(klass) obj = klass.__new__(klass, *args) state = unpickler.load() obj.__setstate__(state) # set other attributes obj._p_jar=OFS.History.HystoryJar(self._conn) obj._p_oid=oid obj._p_serial=self._tid obj._p_changed=0 return obj def _get_unpickler(self, pickle): file = cStringIO.StringIO(pickle) unpickler = cPickle.Unpickler(file) unpickler.persistent_load = self._persistent_load factory = self._factory conn = self._conn def find_global(modulename, name): print 'in find global\n' return factory(conn, modulename, name) unpickler.find_global = find_global return unpickler def _get_class(self, module, name): return self._factory(self._conn, module, name) def _persistent_load(self, oid): print 'in persistent load\n' if isinstance(oid, list): # weakref [oid] = oid obj = WeakRef.__new__(WeakRef) obj.oid = oid obj.dm = self._conn return obj elif isinstance(oid, tuple): oid = oid[0] return self._get_object(oid) ------------------------------------------------------- This SF.Net email is sponsored by xPML, a groundbreaking scripting language that extends applications into web and mobile media. Attend the live webcast and join the prime developer group breaking into this new coding territory! http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642