Thanks David, you are correct.  I now am testing in the correct order,
and everything works as expected for a simple attribute.  Where I have
been runing awry is when the the attribute is a class.  I have assembled
another example below.  Thanks also to Tres Sever who suggested the
following with the first example I sent:
You are mutating the object *inside* your __setstate__: the ZODB
persistence machinery clears the '_p_changed' flag after you *exit* from
'__setstate__': the protocol is not intended to support a persistent
I can accept this, but after returning from the __setstate__, I further
modify the object and commit the transaction (see test_2_setstate).
This should persist as I understand it.  Why does it not?  The reason I
am persisting with this is the __setstate__ offers an elegant way to
upgrade existing objects--something I find myself doing often.
import transaction
import unittest
from persistent import Persistent
from ZODB import FileStorage, DB
TMP_DB = 'zodb-test-filestorage.fs'
class Color(Persistent):
    def __init__(self):
        self.color = 'blue'
    def setColor(self, color):
        self.color = color
    def getColor(self):
        return self.color
class User(Persistent):
    def __init__(self):
    def __setstate__(self, state):
        Persistent.__setstate__(self, state)
        if not hasattr(self, 'color'):
            print 'adding color'
            self.color = Color()
    def getColor(self):
        return self.color.getColor()
    def setColor(self, color):
class ZODB_TestCase(unittest.TestCase):
    def setUp(self):
        storage = FileStorage.FileStorage(TMP_DB)
        self.db = DB(storage)
        conn =
        dbroot = conn.root()
        # Ensure that a 'userdb' key is present
        # in the root
        if not dbroot.has_key('userdb'):
            from BTrees.OOBTree import OOBTree
            dbroot['userdb'] = OOBTree()
        self.userdb = dbroot['userdb']
 = 'amk'
    def tearDown(self):
    def test_1_AddUser(self):
        print 'in test1'
        newuser = User()
        newuser.first_name = 'Andrew'; newuser.last_name = 'Kuchling'
        # Add object to the BTree, keyed on the ID
        self.userdb[] = newuser
        # Commit the change
        # setstate is not called after the constructor
        assert not hasattr(newuser, 'color')
    def test_2_setstate(self):
        print 'in test2'
        newuser = self.userdb[]
        # setstate is called subsequently
        assert hasattr(newuser, 'color')
        assert newuser.getColor() == 'blue'
        assert newuser.getColor() == 'red'        
    def test_3_persistence(self):
        print 'in test3'
        newuser = self.userdb[]
        assert newuser.getColor() == 'red'
if __name__ == '__main__':
    import os
    if os.path.exists(TMP_DB):


Hi David,

It looks like you might be assuming that the tests run in the order
you wrote them.  The test_persistence() function actually gets called
before the test_setstate(), so there is no chance for the color to be

I don't think the failure of this particular test has anything to do
your __setstate__() method.  When I write __setstate__() methods,
though,  I modify the state dictionary and then call
This avoids triggering the persistent attribute machinery at all.

David Binger

For more information about ZODB, see the ZODB Wiki:

ZODB-Dev mailing list  -

Reply via email to