Beispiel #1
0
class OOBTreeState(object):
    """Non-empty OOBTrees have a complicated tuple structure."""

    def __init__(self, type, state, tid):
        self.btree = OOBTree()
        self.btree.__setstate__(state)
        self.state = state
        # Large btrees have more than one bucket; we have to load old states
        # to all of them.  See BTreeTemplate.c and BucketTemplate.c for
        # docs of the pickled state format.
        while state and len(state) > 1:
            bucket = state[1]
            state = IObjectHistory(bucket).loadState(tid)
            # XXX this is dangerous!
            bucket.__setstate__(state)

        self._items = list(self.btree.items())
        self._dict = dict(self.btree)

        # now UNDO to avoid dangerous side effects,
        # see https://bugs.launchpad.net/zodbbrowser/+bug/487243
        state = self.state
        while state and len(state) > 1:
            bucket = state[1]
            state = IObjectHistory(bucket).loadState()
            bucket.__setstate__(state)

    def getError(self):
        return None

    def getName(self):
        return None

    def getParent(self):
        return None

    def listAttributes(self):
        return None

    def listItems(self):
        return self._items

    def asDict(self):
        return self._dict
class OOBTreeState(object):
    """Non-empty OOBTrees have a complicated tuple structure."""
    def __init__(self, type, state, tid):
        self.btree = OOBTree()
        self.btree.__setstate__(state)
        self.state = state
        # Large btrees have more than one bucket; we have to load old states
        # to all of them.  See BTreeTemplate.c and BucketTemplate.c for
        # docs of the pickled state format.
        while state and len(state) > 1:
            bucket = state[1]
            state = IObjectHistory(bucket).loadState(tid)
            # XXX this is dangerous!
            bucket.__setstate__(state)

        self._items = list(self.btree.items())
        self._dict = dict(self.btree)

        # now UNDO to avoid dangerous side effects,
        # see https://bugs.launchpad.net/zodbbrowser/+bug/487243
        state = self.state
        while state and len(state) > 1:
            bucket = state[1]
            state = IObjectHistory(bucket).loadState()
            bucket.__setstate__(state)

    def getError(self):
        return None

    def getName(self):
        return None

    def getParent(self):
        return None

    def listAttributes(self):
        return None

    def listItems(self):
        return self._items

    def asDict(self):
        return self._dict
    def _constructAnnotatedHistory(self, max=10):
        """Reconstruct historical revisions of archetypes objects

        Merges revisions to self with revisions to archetypes-related items
        in __annotations__. Yields at most max recent revisions.

        """
        # All relevant historical states by transaction id
        # For every tid, keep a dict with object revisions, keyed on annotation
        # id, or None for self and '__annotations__' for the ann OOBTree
        # Initialize with self revisions
        history = dict([(tid, {None: rev})
                        for (tid, rev) in _objectRevisions(self, max)])

        if not getattr(self, '__annotations__', None):
            # No annotations, just return the history we have for self
            # Note that if this object had __annotations__ in a past
            # transaction they will be ignored! Working around this is a
            # YAGNI I think though.
            for tid in sorted(history.keys()):
                yield history[tid][None]
            return

        # Now find all __annotation__ revisions, and the annotation keys
        # used in those.
        annotation_key_objects = {}
        isatkey = lambda k, aak=AT_ANN_KEYS: filter(k.startswith, aak)
        # Loop over max revisions of the __annotations__ object to retrieve
        # all keys (and more importantly, their objects so we can get
        # revisions)
        for tid, rev in _objectRevisions(self.__annotations__, max):
            history.setdefault(tid, {})['__annotations__'] = rev
            revision = rev['object']
            for key in itertools.ifilter(isatkey, revision.iterkeys()):
                if not hasattr(revision[key], '_p_jar'):
                    continue  # Not persistent
                if key not in annotation_key_objects:
                    annotation_key_objects[key] = revision[key]

        # For all annotation keys, get their revisions
        for key, obj in annotation_key_objects.iteritems():
            for tid, rev in _objectRevisions(obj, max):
                history.setdefault(tid, {})[key] = rev
        del annotation_key_objects

        # Now we merge the annotation and object revisions into one for each
        # transaction id, and yield the results
        tids = sorted(history.iterkeys(), reverse=True)

        def find_revision(tids, key):
            """First revision of key in a series of tids"""
            has_revision = lambda t, h=history, k=key: k in h[t]
            next_tid = itertools.ifilter(has_revision, tids).next()
            return history[next_tid][key]

        for i, tid in enumerate(tids[:max]):
            revision = find_revision(tids[i:], None)
            obj = revision['object']
            # Track size to maintain correct metadata
            size = revision['size']

            anns_rev = find_revision(tids[i:], '__annotations__')
            size += anns_rev['size']
            anns = anns_rev['object']

            # We use a temporary OOBTree to avoid _p_jar complaints from the
            # transaction machinery
            tempbtree = OOBTree()
            tempbtree.__setstate__(anns.__getstate__())

            # Find annotation revisions and insert
            for key in itertools.ifilter(isatkey, tempbtree.iterkeys()):
                if not hasattr(tempbtree[key], '_p_jar'):
                    continue  # Not persistent
                value_rev = find_revision(tids[i:], key)
                size += value_rev['size']
                tempbtree[key] = value_rev['object']

            # Now transfer the tembtree state over to anns, effectively
            # bypassing the transaction registry while maintaining BTree
            # integrity
            anns.__setstate__(tempbtree.__getstate__())
            anns._p_changed = 0
            del tempbtree

            # Do a similar hack to set anns on the main object
            state = obj.__getstate__()
            state['__annotations__'] = anns
            obj.__setstate__(state)
            obj._p_changed = 0

            # Update revision metadata if needed
            if revision['tid'] != tid:
                # any other revision will do; only size and object are unique
                revision = history[tid].values()[0].copy()
                revision['object'] = obj

            # Correct size based on merged records
            revision['size'] = size

            # clean up as we go
            del history[tid]

            yield revision
Beispiel #4
0
 def __setstate__(self, state):
     Persistent.__setstate__(self, state[0])
     OOBTree.__setstate__(self, state[1])
Beispiel #5
0
 def __setstate__(self, state):
     Persistent.__setstate__(self, state[0])
     OOBTree.__setstate__(self, state[1])
Beispiel #6
0
    def _constructAnnotatedHistory(self, max=10):
        """Reconstruct historical revisions of archetypes objects

        Merges revisions to self with revisions to archetypes-related items
        in __annotations__. Yields at most max recent revisions.

        """
        # All relevant historical states by transaction id
        # For every tid, keep a dict with object revisions, keyed on annotation
        # id, or None for self and '__annotations__' for the ann OOBTree
        # Initialize with self revisions
        history = dict([(tid, {None: rev})
                        for (tid, rev) in _objectRevisions(self, max)])

        if not getattr(self, '__annotations__', None):
            # No annotations, just return the history we have for self
            # Note that if this object had __annotations__ in a past
            # transaction they will be ignored! Working around this is a
            # YAGNI I think though.
            for tid in sorted(history.keys()):
                yield history[tid][None]
            return

        # Now find all __annotation__ revisions, and the annotation keys
        # used in those.
        annotation_key_objects = {}
        isatkey = lambda k, aak=AT_ANN_KEYS: filter(k.startswith, aak)
        # Loop over max revisions of the __annotations__ object to retrieve
        # all keys (and more importantly, their objects so we can get revisions)
        for tid, rev in _objectRevisions(self.__annotations__, max):
            history.setdefault(tid, {})['__annotations__'] = rev
            revision = rev['object']
            for key in itertools.ifilter(isatkey, revision.iterkeys()):
                if not hasattr(revision[key], '_p_jar'):
                    continue  # Not persistent
                if key not in annotation_key_objects:
                    annotation_key_objects[key] = revision[key]

        # For all annotation keys, get their revisions
        for key, obj in annotation_key_objects.iteritems():
            for tid, rev in _objectRevisions(obj, max):
                history.setdefault(tid, {})[key] = rev
        del annotation_key_objects

        # Now we merge the annotation and object revisions into one for each
        # transaction id, and yield the results
        tids = sorted(history.iterkeys(), reverse=True)
        def find_revision(tids, key):
            # First revision of key in a series of tids.
            has_revision = lambda t, h=history, k=key: k in h[t]
            next_tid = itertools.ifilter(has_revision, tids).next()
            return history[next_tid][key]

        for i, tid in enumerate(tids[:max]):
            revision = find_revision(tids[i:], None)
            obj = revision['object']
            # Track size to maintain correct metadata
            size = revision['size']

            anns_rev = find_revision(tids[i:], '__annotations__')
            size += anns_rev['size']
            anns = anns_rev['object']

            # We use a temporary OOBTree to avoid _p_jar complaints from the
            # transaction machinery
            tempbtree = OOBTree()
            tempbtree.__setstate__(anns.__getstate__())

            # Find annotation revisions and insert
            for key in itertools.ifilter(isatkey, tempbtree.iterkeys()):
                if not hasattr(tempbtree[key], '_p_jar'):
                    continue  # Not persistent
                value_rev = find_revision(tids[i:], key)
                size += value_rev['size']
                tempbtree[key] = value_rev['object']

            # Now transfer the tembtree state over to anns, effectively
            # bypassing the transaction registry while maintaining BTree
            # integrity
            anns.__setstate__(tempbtree.__getstate__())
            anns._p_changed = 0
            del tempbtree

            # Do a similar hack to set anns on the main object
            state = obj.__getstate__()
            state['__annotations__'] = anns
            obj.__setstate__(state)
            obj._p_changed = 0

            # Update revision metadata if needed
            if revision['tid'] != tid:
                # any other revision will do; only size and object are unique
                revision = history[tid].values()[0].copy()
                revision['object'] = obj

            # Correct size based on merged records
            revision['size'] = size

            # clean up as we go
            del history[tid]

            yield revision