Get a Snapshot of an Object¶
The lazr.lifecycle.event.ObjectModifiedEvent has an attribute giving the initial state of the object before the modifications. The Snapshot class can be used to easily represent such states:
>>> from zope.interface import Attribute, Interface, implementer
>>> from zope.schema import List, TextLine, Text
>>> from lazr.lifecycle.snapshot import doNotSnapshot, Snapshot
>>> class IFoo(Interface):
... title = TextLine(title=u'My Title')
... description = Text(title=u'Description')
... remotes = List(title=u'remotes')
... totals = Attribute('totals')
... ignore_me = doNotSnapshot(Attribute('ignored attribute'))
>>> @implementer(IFoo)
... class Foo:
... remotes = ["OK"]
...
... @property
... def totals(self):
... return "NOT"
>>> foo = Foo()
>>> foo.title = 'Some Title'
>>> foo.description = 'bla bla bla'
A snapshot can be created by specifying the names of the attribute you want to snapshot:
>>> snapshot = Snapshot(foo, names=['title'])
Only the given attributes will be assigned to the snapshot:
>>> snapshot.title == foo.title
True
>>> hasattr(snapshot, 'description')
False
The snapshot won’t provide the same interface as foo, though:
>>> IFoo.providedBy(snapshot)
False
If we want the snapshot to provide some interface, we have to specify that explicitly:
>>> snapshot = Snapshot(foo, names=['title'], providing=IFoo)
>>> snapshot.title == foo.title
True
>>> hasattr(snapshot, 'description')
False
>>> IFoo.providedBy(snapshot)
True
The API requires you to specify either ‘names’ or ‘providing’:
>>> snapshot = Snapshot(foo)
Traceback (most recent call last):
...
SnapshotCreationError
If no names argument is supplied, the snapshot will contain all IFields of the specified interface(s).
>>> snapshot = Snapshot(foo, providing=IFoo)
>>> snapshot.title == foo.title
True
>>> hasattr(snapshot, 'description')
True
Totals is not a Fields so isn’t copied over.
>>> hasattr(snapshot, 'totals')
False
>>> hasattr(snapshot, 'remotes')
True
>>> snapshot.remotes == ["OK"]
True
Fields can be explicitly ignored if they provide the IDoNotSnapshot interface.
>>> hasattr(snapshot, 'ignore_me')
False
We can also give more than one interface to provide as an iterable. If we don’t specify any names, all the names in the given interfaces will be copied:
>>> from zope.interface import providedBy
>>> snapshot = Snapshot(foo, providing=providedBy(foo))
>>> snapshot.title == foo.title
True
>>> snapshot.description == foo.description
True
>>> IFoo.providedBy(snapshot)
True
>>> class IBar(Interface):
... name = Text(title=u'Name')
>>> foo.name = "barbie"
>>> snapshot = Snapshot(foo, providing=[IFoo, IBar])
>>> IFoo.providedBy(snapshot)
True
>>> IBar.providedBy(snapshot)
True
>>> snapshot.title == foo.title
True
>>> snapshot.name == "barbie"
True
>>> snapshot.description == foo.description
True
>>> IFoo.providedBy(snapshot)
True
>>> IBar.providedBy(snapshot)
True
ISnapshotValueFactory¶
For some fields, assigning the existing value to the snapshot object isn’t appropriate. For these case, one can provide a factory registered as an adapter for the value to ISnapshotValueFactory. The result of the adaptation lookup will be stored in the snapshot attribute.
>>> from zope.interface import implementer, Interface
>>> from zope.component import adapter, getSiteManager
>>> class IIterable(Interface):
... """Marker for a value that needs a special snapshot."""
>>> @implementer(IIterable)
... class EvenOrOddIterable:
... """An object that will be snapshotted specially."""
... even = True
... max = 10
...
... def __iter__(self):
... for i in range(self.max):
... if i % 2 == 0 and self.even:
... yield i
... elif i % 2 == 1 and not self.even:
... yield i
... else:
... continue
>>> from lazr.lifecycle.interfaces import ISnapshotValueFactory
>>> @implementer(ISnapshotValueFactory)
... @adapter(IIterable)
... def snapshot_iterable(value):
... return list(value)
>>> getSiteManager().registerAdapter(snapshot_iterable)
>>> foo = Foo()
>>> foo.title = 'Even'
>>> foo.description = 'Generates even number below 10.'
>>> foo.remotes = EvenOrOddIterable()
>>> snapshot = Snapshot(foo, providing=IFoo)
>>> snapshot.remotes == list(foo.remotes)
True
>>> getSiteManager().unregisterAdapter(snapshot_iterable)
True