Writing persistent classes Persistent if reachable from the root Persistency by storing/loading...

14
Writing persistent classes • Persistent if reachable from the root • Persistency by storing/loading pickles • ZODB must know when an object is accessed or changed • Automatic (transparent) for attribute access • Some common Python idioms require explicit interactions

Transcript of Writing persistent classes Persistent if reachable from the root Persistency by storing/loading...

Page 1: Writing persistent classes Persistent if reachable from the root Persistency by storing/loading pickles ZODB must know when an object is accessed or changed.

Writing persistent classes

• Persistent if reachable from the root

• Persistency by storing/loading pickles

• ZODB must know when an object is accessed or changed

• Automatic (transparent) for attribute access

• Some common Python idioms require explicit interactions

Page 2: Writing persistent classes Persistent if reachable from the root Persistency by storing/loading pickles ZODB must know when an object is accessed or changed.

Persistence by reachability

• Persistent object must be reachable from the root object, which ZODB creates automatically

myPersistentObj = PersistentObj()

conn.root()[‘coll’] = myPersistentObject

Page 3: Writing persistent classes Persistent if reachable from the root Persistency by storing/loading pickles ZODB must know when an object is accessed or changed.

Saved state is a pickle

• Objects to be stored in ZODB must be picklable.

• Normally, an object’s attributes (__dict__) are pickled by ZODB for storing, and the pickle is unserialized into __dict__ for loading.

• Persistent captures all attribute access via __getattr__() and __setattr__() hooks

Page 4: Writing persistent classes Persistent if reachable from the root Persistency by storing/loading pickles ZODB must know when an object is accessed or changed.

Subobjects

• Subobjects are pickled by value, except:– if they are Persistent, they’re pickled by “persistent id”

– Classes, modules, and functions are pickled by “fully qualified name”

– Upon unpickling instances, __init__() is not called unless the class defines an __getinitargs__() method.

– See the Python 2.2 docs for pickle module for more rules regarding extension types, etc.

Page 5: Writing persistent classes Persistent if reachable from the root Persistency by storing/loading pickles ZODB must know when an object is accessed or changed.

Mutable attributes

• Object has an attribute that’s a mutable Python object, e.g. dictionary or list

• Changes to the mutable object are not caught by ZODB, so you have to help it out

>>> person.appointments

[]

>>> person.appointments.append(app)

>>> person._p_changed = 1

Page 6: Writing persistent classes Persistent if reachable from the root Persistency by storing/loading pickles ZODB must know when an object is accessed or changed.

PersistentMapping

• Class provided by ZODB for persistent, near-dictionary-like semantics

• It fiddles with _p_changed for you:>>> person.contacts

<PersistentMapping instance at 81445d8>

>>> person.contacts[‘Barry’] = barry

>>> get_transaction().commit()

Page 7: Writing persistent classes Persistent if reachable from the root Persistency by storing/loading pickles ZODB must know when an object is accessed or changed.

PersistentList?

• Not part of Zope Corp’s distribution (yet)

• Andrew Kuchling’s SourceForge project has one (zodb.sf.net)

• Provides list-like semantics while taking care of _p_changed fiddling

Page 8: Writing persistent classes Persistent if reachable from the root Persistency by storing/loading pickles ZODB must know when an object is accessed or changed.

Unpicklable objects

class F(Persistent):

def __init__(self, filename):

self.fp = open(filename)

>>> root[‘files’] = F(‘/etc/passwd’)

>>> get_transaction().commit()

cPickle.UnpicklableError: cannot pickle <type ‘file’> objects

Page 9: Writing persistent classes Persistent if reachable from the root Persistency by storing/loading pickles ZODB must know when an object is accessed or changed.

Unpicklable objects

class F(Persistent):def __init__(self, filename):

self.fp = open(filename)def __getstate__(self):

return self.fp.namedef __setstate__(self, filename):

self.fp = open(filename)>>> root[‘files’] = F(‘/etc/passwd’)>>> get_transaction().commit()>>>

Page 10: Writing persistent classes Persistent if reachable from the root Persistency by storing/loading pickles ZODB must know when an object is accessed or changed.

Volatile attributes

• Attributes not to be stored persistently should be prefixed with _v_

class F(Persistent):

def __init__(self, filename):

self._v_fp = open(filename)

>>> root[‘files’] = F(‘/etc/passwd’)

>>> get_transaction().commit()

# later…

>>> root[‘files’].__dict__

{}

Page 11: Writing persistent classes Persistent if reachable from the root Persistency by storing/loading pickles ZODB must know when an object is accessed or changed.

Python special methods

• Persistent classes can’t define reversed binary operators like __radd__ and __rsub__

• Older versions had some problems with __cmp__() and (maybe) __hash__()

• Persistent classes won’t track new __ methods introduced in Python 2.2 and beyond.

Page 12: Writing persistent classes Persistent if reachable from the root Persistency by storing/loading pickles ZODB must know when an object is accessed or changed.

Managing object evolution

• Classes can change by adding, deleting, or redefining methods, or adding and deleting attributes.

• Changes to methods “just work” because classes are stored by reference, not by value

• Changes to attributes can be handled by __setstate__() method, or by off-line update script

Page 13: Writing persistent classes Persistent if reachable from the root Persistency by storing/loading pickles ZODB must know when an object is accessed or changed.

__setstate__()

class Person(Persistent):

def __init__(self, name):

self.name = name

>>> barry = Person(‘Barry Warsaw’)

>>> root[‘people’][‘barry’] = barry

>>> get_transaction().commit()

Page 14: Writing persistent classes Persistent if reachable from the root Persistency by storing/loading pickles ZODB must know when an object is accessed or changed.

__setstate__() con’t

class Person(Persistent):

def __init__(self, username, realname):

self.username = username

self.realname = realname

def __setstate__(self, d):

self.realname = name = d[‘name’]

username = name.split()[0].lower()

self.username = username