[ZODB-Dev] __setstate__() and _p_changed

Greg Ward gward@mems-exchange.org
Tue, 15 May 2001 13:33:33 -0400


Hi --

I'm trying to understand how __setstate__() methods interact with
_p_changed attributes.  We write a lot of __setstate__()'s to handle
database evolution: if a class adds an attribute foo, then we add this
to the class:

    def __setstate__ (self, dict):
        self.__dict__.update(dict)
        if not hasattr(self, 'foo'):
            self.foo = None

in order to transparently add the new attribute to old objects.  So far,
this has worked great.

But now I have a class where it's *not* working, and I don't understand
why.  Here's the original __setstate__() method:

    def __setstate__ (self, dict):
        self.__dict__.update(dict)

        if not hasattr(self, '_process_name_idx'):
            self._process_name_idx = BTree.BTree()

The trouble is that _p_changed is *not* being set on this object, even
though it's obviously being changed (through a setattr).  Or rather, I
should say that _p_changed is 0 after __setstate__() completes.  Here's
an instrumented __setstate__():

    def __setstate__ (self, dict):
        self.__dict__.update(dict)
        if not hasattr(self, '_process_name_idx'):
            print "ProcessLibrary.__setstate__: creating _process_name_idx"
            print "  _p_changed =", self._p_changed
            self._process_name_idx = BTree.BTree()
            print "  _p_changed =", self._p_changed

and here's what happens when I access the object in an interactive
session:

>>> connection.root()['process_lib']
ProcessLibrary.__setstate__: creating _process_name_idx
  _p_changed = 1
  _p_changed = 1
<ProcessLibrary at 82a35f8>

Note that __setstate__() did what it said:

>>> process_lib._process_name_idx
<BTree object at 0x8282050>
>>> len(process_lib._process_name_idx)
0

but _p_changed has become false:
>>> process_lib._p_changed
0

Factors that may or may not be relevant:
  * the class in question (ProcessLibrary) is a root class, ie.
    "connection.root()['process_lib']"
    returns the sole instance of ProcessLibrary in our database
    (connection is a ZODB.Connection.Connection instance, and
    connection.root() is a PersistentMapping with eight
    "root objects" in it)
  * the attribute in question starts with "_" -- although I tried it
    without the underscore and got the same behaviour
  * the value in question is a BTree -- although I tried it with
    an ordinary dictionary and a plain integer and got the same
    behaviour

I don't think it's the attribute name or value -- if I change the attr
assignment to "self.foo = 37", I have the same problem.

I wonder if there is something I should know about __setstate__() on
"root objects", though -- we use this trick a lot, and _p_changed seems
to be properly set on non-root objects after a __setstate__() that makes
changes.  What's going on here?

        Greg
-- 
Greg Ward - software developer                gward@mems-exchange.org
MEMS Exchange                            http://www.mems-exchange.org