lazr.lifecycle-1.0/ 0000755 0001750 0001750 00000000000 11246761533 014466 5 ustar leonardr leonardr lazr.lifecycle-1.0/src/ 0000755 0001750 0001750 00000000000 11246761533 015255 5 ustar leonardr leonardr lazr.lifecycle-1.0/src/lazr/ 0000755 0001750 0001750 00000000000 11246761533 016225 5 ustar leonardr leonardr lazr.lifecycle-1.0/src/lazr/lifecycle/ 0000755 0001750 0001750 00000000000 11246761533 020164 5 ustar leonardr leonardr lazr.lifecycle-1.0/src/lazr/lifecycle/docs/ 0000755 0001750 0001750 00000000000 11246761533 021114 5 ustar leonardr leonardr lazr.lifecycle-1.0/src/lazr/lifecycle/docs/__init__.py 0000644 0001750 0001750 00000001345 11246761142 023224 0 ustar leonardr leonardr # Copyright 2009 Canonical Ltd. All rights reserved.
#
# This file is part of lazr.lifecycle
#
# lazr.lifecycle is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# lazr.lifecycle is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with lazr.lifecycle. If not, see .
"The lazr.lifecycle documents."
lazr.lifecycle-1.0/src/lazr/lifecycle/docs/event.txt 0000644 0001750 0001750 00000005306 11246761142 022776 0 ustar leonardr leonardr LAZR Lifecycle Events
=====================
lazr.lifecycle defines common lifecycle events. They are extensions of the
base Z3 lifecycle events.
>>> from lazr.lifecycle.interfaces import (
... IObjectCreatedEvent, IObjectModifiedEvent,
... IObjectDeletedEvent)
>>> from lazr.lifecycle.event import (
... ObjectCreatedEvent, ObjectDeletedEvent, ObjectModifiedEvent)
>>> from zope.interface.verify import verifyObject
>>> class Fnord:
... "A Fnort sighting."""
...
... def __init__(self, who, where):
... self.who = who
... self.where = where
The module defines three lifecycle events:
* ObjectCreatedEvent - Used when an object has been created.
* ObjectModifiedEvent - Used then an object has been modified, it
provides meta data explaining what was modified.
* ObjectDeletedEvent - Used when the object was deleted.
All events expose the user responsible for the change in the user
attribute. By default, if not specified when constructing the event,
this will be the principal associated with the interaction. (Only one
principal is currently supported)
>>> from zope.security.management import (
... newInteraction, endInteraction)
>>> class MyParticipation:
... def __init__(self, who):
... self.principal = who
... self.interaction = None
>>> newInteraction(MyParticipation('the user'))
>>> fnord = Fnord('me', 'the_bridge')
>>> created_event = ObjectCreatedEvent(fnord)
>>> verifyObject(IObjectCreatedEvent, created_event)
True
>>> print created_event.user
the user
The object attribute contains the object that was created:
>>> created_event.object is fnord
True
The ObjectModifiedEvent holds the names of the modified fields in the
'edited_fields' attribute and the state of the object before the
modifications in 'object_before_modification' attribute. (See
snapshot.txt for a way to manage that easily).
>>> snapshot = Fnord('me', 'the_bridge')
>>> fnord.who = 'someone else'
>>> modified_event = ObjectModifiedEvent(fnord, snapshot, ['who'])
>>> verifyObject(IObjectModifiedEvent, modified_event)
True
>>> modified_event.edited_fields
['who']
>>> modified_event.object is fnord
True
>>> print modified_event.object_before_modification.who
me
>>> print modified_event.user
the user
The ObjectDeletedEvent is used to broadcast the deletion of the object.
>>> deleted_event = ObjectDeletedEvent(fnord, user='the_censor')
>>> verifyObject(IObjectDeletedEvent, deleted_event)
True
>>> deleted_event.object is fnord
True
>>> print deleted_event.user
the_censor
lazr.lifecycle-1.0/src/lazr/lifecycle/docs/object-delta.txt 0000644 0001750 0001750 00000006263 11246761142 024215 0 ustar leonardr leonardr Creating Object Deltas
======================
A common workflow surrounding life-cycle events involves computing the deltas
between two objects instances. This is normally for notification emails in
order to identify what has been changed, so the appropriate information is
sent out in the email.
In order to test that the deltas, we need a simple object to test.
>>> class TestObj(object):
... def __init__(self, vars):
... self.__dict__.update(vars)
>>> old_obj = TestObj({'foo':42, 'bar':'hello', 'baz':[1,2,3], 'wiz':1})
>>> new_obj = TestObj({'foo':42, 'bar':'world', 'baz':[2,3,4], 'wiz':2})
>>> old_obj.foo
42
>>> old_obj.bar
'hello'
And here is a simple helper function that we'll use to
print out dicts in sorted order (as pprint doesn't assure this until 2.5).
>>> import types
>>> def display_delta(to_display, indent=0):
... for key in sorted(to_display.keys()):
... value = to_display[key]
... if type(value) == types.DictType:
... print "%s: %s => %s" % (key, repr(value['old']),
... repr(value['new']))
... else:
... print "%s: %s" % (key, repr(to_display[key]))
The ObjectDelta instance is constructed with the old object,
and the new object instances.
>>> from lazr.lifecycle.objectdelta import ObjectDelta
>>> delta = ObjectDelta(old_obj, new_obj)
The simplest case is are recording of new values. The changes property
of the delta object just shows the field name and the new value.
>>> delta.recordNewValues(['foo', 'bar', 'wiz'])
>>> display_delta(delta.changes)
bar: 'world'
wiz: 2
An alternative to just returning the new values is to return
both the old and the new values for a field value.
>>> delta = ObjectDelta(old_obj, new_obj)
>>> delta.recordNewAndOld(['foo', 'bar', 'wiz'])
>>> display_delta(delta.changes)
bar: 'hello' => 'world'
wiz: 1 => 2
In some cases, a list is needed to be checked for additions and removals.
Examples for this would be associated bugs, specs or brances with the
specified object.
>>> delta = ObjectDelta(old_obj, new_obj)
>>> delta.recordListAddedAndRemoved('baz', 'added', 'removed')
>>> display_delta(delta.changes)
added: [4]
removed: [1]
Now the normal usage is to do some combination of the above, and pass
the resulting map through to the constructor for a real delta object.
>>> class TestDelta:
... def __init__(self, foo=None, bar=None, wiz=None,
... baz_added=None, baz_removed=None):
... self.foo = foo
... self.bar = bar
... self.baz_added = baz_added
... self.baz_removed= baz_removed
... self.wiz = wiz
>>> obj_delta = ObjectDelta(old_obj, new_obj)
>>> obj_delta.recordNewValues(['foo', 'bar'])
>>> obj_delta.recordNewAndOld(['wiz'])
>>> obj_delta.recordListAddedAndRemoved('baz', 'baz_added',
... 'baz_removed')
>>> delta = TestDelta(**obj_delta.changes)
>>> delta.foo
>>> delta.bar
'world'
>>> delta.baz_added
[4]
>>> delta.baz_removed
[1]
>>> display_delta(delta.wiz)
new: 2
old: 1
lazr.lifecycle-1.0/src/lazr/lifecycle/docs/snapshot.txt 0000644 0001750 0001750 00000011273 11246761142 023514 0 ustar leonardr leonardr 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 Interface, implements, Attribute
>>> from zope.schema import List, TextLine, Text
>>> from lazr.lifecycle.snapshot import Snapshot
>>> class IFoo(Interface):
... title = TextLine(title=u'My Title')
... description = Text(title=u'Description')
... remotes = List(title=u'remotes')
... totals = Attribute('totals')
>>> class Foo:
... implements(IFoo)
...
... @property
... def remotes(self):
... return ["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
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."""
>>> class EvenOrOddIterable:
... """An object that will be snapshotted specially."""
... implements(IIterable)
...
... 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
lazr.lifecycle-1.0/src/lazr/lifecycle/tests/ 0000755 0001750 0001750 00000000000 11246761533 021326 5 ustar leonardr leonardr lazr.lifecycle-1.0/src/lazr/lifecycle/tests/__init__.py 0000644 0001750 0001750 00000001340 11246761142 023431 0 ustar leonardr leonardr # Copyright 2009 Canonical Ltd. All rights reserved.
#
# This file is part of lazr.lifecycle
#
# lazr.lifecycle is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# lazr.lifecycle is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with lazr.lifecycle. If not, see .
"The lazr.lifecycle tests."
lazr.lifecycle-1.0/src/lazr/lifecycle/tests/test_docs.py 0000644 0001750 0001750 00000004371 11246761142 023670 0 ustar leonardr leonardr # Copyright 2009 Canonical Ltd. All rights reserved.
#
# This file is part of lazr.lifecycle
#
# lazr.lifecycle is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# lazr.lifecycle is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with lazr.lifecycle. If not, see .
"Test harness for doctests."
# pylint: disable-msg=E0611,W0142
__metaclass__ = type
__all__ = [
'additional_tests',
]
import atexit
import doctest
import os
from pkg_resources import (
resource_filename, resource_exists, resource_listdir, cleanup_resources)
import unittest
import warnings
DOCTEST_FLAGS = (
doctest.ELLIPSIS |
doctest.NORMALIZE_WHITESPACE |
doctest.REPORT_NDIFF)
def raise_warning(message, category=None, stacklevel=1):
if category is None:
kind = 'UserWarning'
else:
kind = category.__class__.__name__
print "%s: %s" % (kind, message)
def setUp(test):
"""Makes any warning an error."""
test.globs['saved_warn'] = warnings.warn
warnings.warn = raise_warning
def tearDown(test):
"""Reset the warnings."""
warnings.warn = test.globs['saved_warn']
def additional_tests():
"Run the doc tests (README.txt and docs/*, if any exist)"
doctest_files = [
os.path.abspath(resource_filename('lazr.lifecycle', 'README.txt'))]
if resource_exists('lazr.lifecycle', 'docs'):
for name in resource_listdir('lazr.lifecycle', 'docs'):
if name.endswith('.txt'):
doctest_files.append(
os.path.abspath(
resource_filename('lazr.lifecycle', 'docs/%s' % name)))
kwargs = dict(
setUp=setUp, tearDown=tearDown,
module_relative=False,
optionflags=DOCTEST_FLAGS)
atexit.register(cleanup_resources)
return unittest.TestSuite((
doctest.DocFileSuite(*doctest_files, **kwargs)))
lazr.lifecycle-1.0/src/lazr/lifecycle/NEWS.txt 0000644 0001750 0001750 00000000406 11246761210 021471 0 ustar leonardr leonardr =======================
NEWS for lazr.lifecycle
=======================
1.0 (2009-08-31)
================
- Remove build dependencies on bzr and egg_info
- remove sys.path hack in setup.py for __version__
0.1 (2009-03-24)
================
- Initial release
lazr.lifecycle-1.0/src/lazr/lifecycle/README.txt 0000644 0001750 0001750 00000002264 11246761142 021662 0 ustar leonardr leonardr ..
This file is part of lazr.lifecycle.
lazr.lifecycle is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 3 of the License.
lazr.lifecycle is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with lazr.lifecycle. If not, see .
LAZR lifecycle
**************
This package defines three "lifecycle" events that notify about object
creation, modification and deletion. The events include information about the
user responsible for the changes.
The modification event also includes information about the state of the object
before the changes.
The module also contains Snapshot support to save the state of an object for
notification, and to compute deltas between version of objects.
Other documents
===============
.. toctree::
:glob:
*
docs/*
lazr.lifecycle-1.0/src/lazr/lifecycle/__init__.py 0000644 0001750 0001750 00000001465 11246761210 022273 0 ustar leonardr leonardr # Copyright 2009 Canonical Ltd. All rights reserved.
#
# This file is part of lazr.lifecycle
#
# lazr.lifecycle is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# lazr.lifecycle is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with lazr.lifecycle. If not, see .
import pkg_resources
__version__ = pkg_resources.resource_string(
"lazr.lifecycle", "version.txt").strip()
lazr.lifecycle-1.0/src/lazr/lifecycle/event.py 0000644 0001750 0001750 00000005270 11246761142 021657 0 ustar leonardr leonardr # Copyright 2009 Canonical Ltd. All rights reserved.
#
# This file is part of lazr.lifecycle
#
# lazr.lifecycle is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# lazr.lifecycle is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with lazr.lifecycle. If not, see .
__metaclass__ = type
__all__ = ['ObjectCreatedEvent',
'ObjectDeletedEvent',
'ObjectModifiedEvent',
]
from zope.component import getUtility
from zope.interface import implements
import zope.security.management as management
from lazr.lifecycle.interfaces import (
IObjectCreatedEvent, IObjectModifiedEvent, IObjectDeletedEvent)
class LifecyleEventBase:
"""Base class for all LAZR lifecycle event.
It fills the user attribute based on the current interaction.
"""
def __init__(self, object, user=None):
self.object = object
if user is None:
user = self._getDefaultUser()
self.user = user
def _getDefaultUser(self):
"""Return the principal registered in the interaction.
This raises a ValueError in case there is more than one principal in
the interaction.
"""
interaction = management.queryInteraction()
if interaction is None:
return None
principals = [
participation.principal
for participation in list(interaction.participations)
if participation.principal is not None
]
if len(principals) == 1:
return principals[0]
elif len(principals) > 1:
raise ValueError('Too many principals')
else:
return None
class ObjectCreatedEvent(LifecyleEventBase):
"""See `IObjectCreatedEvent`."""
implements(IObjectCreatedEvent)
class ObjectDeletedEvent(LifecyleEventBase):
"""See `IObjectDeletedEvent`."""
implements(IObjectDeletedEvent)
class ObjectModifiedEvent(LifecyleEventBase):
"""See `ISQLObjectModifiedEvent`."""
implements(IObjectModifiedEvent)
def __init__(self, object, object_before_modification, edited_fields,
user=None):
super(ObjectModifiedEvent, self).__init__(object, user=user)
self.object_before_modification = object_before_modification
self.edited_fields = edited_fields
lazr.lifecycle-1.0/src/lazr/lifecycle/interfaces.py 0000644 0001750 0001750 00000004025 11246761142 022656 0 ustar leonardr leonardr # Copyright 2009 Canonical Ltd. All rights reserved.
#
# This file is part of lazr.lifecycle
#
# lazr.lifecycle is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# lazr.lifecycle is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with lazr.lifecycle. If not, see .
"""Lifecycle-related interfaces."""
__metaclass__ = type
__all__ = [
'IObjectCreatedEvent',
'IObjectDeletedEvent',
'IObjectModifiedEvent',
'ISnapshotValueFactory',
]
import zope.lifecycleevent.interfaces as z3lifecycle
from zope.component.interfaces import IObjectEvent
from zope.interface import Interface, Attribute
class IObjectCreatedEvent(z3lifecycle.IObjectCreatedEvent):
"""An object has been created."""
user = Attribute("The user who created the object.")
class IObjectDeletedEvent(IObjectEvent):
"""An object is being deleted."""
user = Attribute("The user who is making this change.")
class IObjectModifiedEvent(z3lifecycle.IObjectModifiedEvent):
"""An object has been modified."""
object_before_modification = Attribute("The object before modification.")
edited_fields = Attribute(
"The list of fields that were edited. A field name may appear in "
"this list if it were shown on an edit form, but not actually "
"changed.")
user = Attribute("The user who modified the object.")
class ISnapshotValueFactory(Interface):
"""This is a marker interface used to obtain snapshot of values.
The interface isn't meant to be provided, but is only used as a factory
lookup. The snapshot value is what should be returned from the adapter
lookup.
"""
lazr.lifecycle-1.0/src/lazr/lifecycle/objectdelta.py 0000644 0001750 0001750 00000005045 11246761142 023016 0 ustar leonardr leonardr # Copyright 2006 Canonical Ltd. All rights reserved.
#
# This file is part of lazr.lifecycle
#
# lazr.lifecycle is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# lazr.lifecycle is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with lazr.lifecycle. If not, see .
__metaclass__ = type
__all__ = ['ObjectDelta']
class ObjectDelta:
"""A helper object for delta creation."""
def __init__(self, old_obj, new_obj):
self.old_obj = old_obj
self.new_obj = new_obj
self.changes = {}
def recordNewValues(self, fields):
"""Updates changes based on changed field values."""
for field_name in fields:
old_val = getattr(self.old_obj, field_name, None)
new_val = getattr(self.new_obj, field_name, None)
if old_val != new_val:
self.changes[field_name] = new_val
def recordNewAndOld(self, fields):
"""Updates changes with old and new values for changed fields."""
for field_name in fields:
old_val = getattr(self.old_obj, field_name, None)
new_val = getattr(self.new_obj, field_name, None)
if old_val != new_val:
self.changes[field_name] = { 'old' : old_val,
'new' : new_val }
def recordListAddedAndRemoved(self, field, added_name, removed_name):
"""Calculates changes in list style attributes."""
# As much as I'd love to use sets, they are unordered
# and any differences returned are not necessarily
# consistant with the order they are in the underlying object.
old_items = getattr(self.old_obj, field, [])
new_items = getattr(self.new_obj, field, [])
added_items = []
for item in new_items:
if item not in old_items:
added_items.append(item)
if added_items:
self.changes[added_name] = added_items
removed_items = []
for item in old_items:
if item not in new_items:
removed_items.append(item)
if removed_items:
self.changes[removed_name] = removed_items
lazr.lifecycle-1.0/src/lazr/lifecycle/snapshot.py 0000644 0001750 0001750 00000005607 11246761142 022401 0 ustar leonardr leonardr # Copyright 2009 Canonical Ltd. All rights reserved.
#
# This file is part of lazr.lifecycle
#
# lazr.lifecycle is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# lazr.lifecycle is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with lazr.lifecycle. If not, see .
"""Provides object snapshotting functionality.
This is particularly useful in calculating deltas.
"""
__metaclass__ = type
__all__ = ['SnapshotCreationError',
'Snapshot',
]
from zope.component import queryAdapter
from zope.interface.interfaces import IInterface
from zope.interface import directlyProvides
from zope.schema.interfaces import IField
from zope.security.proxy import removeSecurityProxy
from lazr.lifecycle.interfaces import ISnapshotValueFactory
_marker = object()
class SnapshotCreationError(Exception):
"""Something went wrong while creating a snapshot."""
class Snapshot:
"""Provides a simple snapshot of the given object.
The snapshot will have the attributes listed in names. It
will also provide the interfaces listed in providing. If no names
are supplied but an interface is provided, all Fields of that
interface will be included in the snapshot.
The attributes are copied by passing them through a ISnapshotValueFactory.
The default implementation of that adapter just returns the value itself.
"""
def __init__(self, ob, names=None, providing=None):
ob = removeSecurityProxy(ob)
if names is None and providing is None:
raise SnapshotCreationError(
"You have to specify either 'names' or 'providing'.")
if IInterface.providedBy(providing):
providing = [providing]
if names is None:
names = set()
for iface in providing:
for name in iface.names(all=True):
field = iface[name]
if IField.providedBy(field):
names.add(name)
for name in names:
value = getattr(ob, name, _marker)
if value is _marker:
raise AssertionError("Attribute %s not in object %r"
% (name, ob))
snapshot_value = queryAdapter(
value, ISnapshotValueFactory, default=_marker)
if snapshot_value is _marker:
snapshot_value = value
setattr(self, name, snapshot_value)
if providing is not None:
directlyProvides(self, providing)
lazr.lifecycle-1.0/src/lazr/lifecycle/version.txt 0000644 0001750 0001750 00000000004 11246761210 022374 0 ustar leonardr leonardr 1.0
lazr.lifecycle-1.0/src/lazr/__init__.py 0000644 0001750 0001750 00000001617 11246761142 020337 0 ustar leonardr leonardr # Copyright 2009 Canonical Ltd. All rights reserved.
#
# This file is part of lazr.lifecycle.
#
# lazr.lifecycle is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# lazr.lifecycle is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with lazr.lifecycle. If not, see .
# this is a namespace package
try:
import pkg_resources
pkg_resources.declare_namespace(__name__)
except ImportError:
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)
lazr.lifecycle-1.0/src/lazr.lifecycle.egg-info/ 0000755 0001750 0001750 00000000000 11246761533 021655 5 ustar leonardr leonardr lazr.lifecycle-1.0/src/lazr.lifecycle.egg-info/PKG-INFO 0000644 0001750 0001750 00000004607 11246761533 022761 0 ustar leonardr leonardr Metadata-Version: 1.0
Name: lazr.lifecycle
Version: 1.0
Summary: Richer lifecycle events API.
Home-page: https://launchpad.net/lazr.lifecycle
Author: LAZR Developers
Author-email: lazr-developers@lists.launchpad.net
License: LGPL v3
Download-URL: https://launchpad.net/lazr.yourpkg/+download
Description: ..
This file is part of lazr.lifecycle.
lazr.lifecycle is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 3 of the License.
lazr.lifecycle is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with lazr.lifecycle. If not, see .
LAZR lifecycle
**************
This package defines three "lifecycle" events that notify about object
creation, modification and deletion. The events include information about the
user responsible for the changes.
The modification event also includes information about the state of the object
before the changes.
The module also contains Snapshot support to save the state of an object for
notification, and to compute deltas between version of objects.
Other documents
===============
.. toctree::
:glob:
*
docs/*
=======================
NEWS for lazr.lifecycle
=======================
1.0 (2009-08-31)
================
- Remove build dependencies on bzr and egg_info
- remove sys.path hack in setup.py for __version__
0.1 (2009-03-24)
================
- Initial release
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
lazr.lifecycle-1.0/src/lazr.lifecycle.egg-info/SOURCES.txt 0000644 0001750 0001750 00000001466 11246761533 023550 0 ustar leonardr leonardr README.txt
ez_setup.py
setup.py
src/lazr/__init__.py
src/lazr.lifecycle.egg-info/PKG-INFO
src/lazr.lifecycle.egg-info/SOURCES.txt
src/lazr.lifecycle.egg-info/dependency_links.txt
src/lazr.lifecycle.egg-info/namespace_packages.txt
src/lazr.lifecycle.egg-info/not-zip-safe
src/lazr.lifecycle.egg-info/requires.txt
src/lazr.lifecycle.egg-info/top_level.txt
src/lazr/lifecycle/NEWS.txt
src/lazr/lifecycle/README.txt
src/lazr/lifecycle/__init__.py
src/lazr/lifecycle/event.py
src/lazr/lifecycle/interfaces.py
src/lazr/lifecycle/objectdelta.py
src/lazr/lifecycle/snapshot.py
src/lazr/lifecycle/version.txt
src/lazr/lifecycle/docs/__init__.py
src/lazr/lifecycle/docs/event.txt
src/lazr/lifecycle/docs/object-delta.txt
src/lazr/lifecycle/docs/snapshot.txt
src/lazr/lifecycle/tests/__init__.py
src/lazr/lifecycle/tests/test_docs.py lazr.lifecycle-1.0/src/lazr.lifecycle.egg-info/dependency_links.txt 0000644 0001750 0001750 00000000001 11246761533 025723 0 ustar leonardr leonardr
lazr.lifecycle-1.0/src/lazr.lifecycle.egg-info/namespace_packages.txt 0000644 0001750 0001750 00000000005 11246761533 026203 0 ustar leonardr leonardr lazr
lazr.lifecycle-1.0/src/lazr.lifecycle.egg-info/not-zip-safe 0000644 0001750 0001750 00000000001 11246761533 024103 0 ustar leonardr leonardr
lazr.lifecycle-1.0/src/lazr.lifecycle.egg-info/requires.txt 0000644 0001750 0001750 00000000156 11246761533 024257 0 ustar leonardr leonardr setuptools
zope.interface
zope.component
zope.lifecycleevent
zope.security
[docs]
Sphinx
z3c.recipe.sphinxdoc lazr.lifecycle-1.0/src/lazr.lifecycle.egg-info/top_level.txt 0000644 0001750 0001750 00000000005 11246761533 024402 0 ustar leonardr leonardr lazr
lazr.lifecycle-1.0/README.txt 0000644 0001750 0001750 00000001300 11246761142 016152 0 ustar leonardr leonardr Richer lifecycle events API.
..
This file is part of lazr.lifecycle.
lazr.lifecycle is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 3 of the License.
lazr.lifecycle is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with lazr.lifecycle. If not, see .
lazr.lifecycle-1.0/ez_setup.py 0000644 0001750 0001750 00000022466 11246761142 016704 0 ustar leonardr leonardr #!python
"""Bootstrap setuptools installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from ez_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import sys
DEFAULT_VERSION = "0.6c8"
DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
md5_data = {
'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
}
import sys, os
def _validate_md5(egg_name, data):
if egg_name in md5_data:
from md5 import md5
digest = md5(data).hexdigest()
if digest != md5_data[egg_name]:
print >>sys.stderr, (
"md5 validation of %s failed! (Possible download problem?)"
% egg_name
)
sys.exit(2)
return data
def use_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
download_delay=15, min_version=None
):
"""Automatically find/download setuptools and make it available on sys.path
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end with
a '/'). `to_dir` is the directory where setuptools will be downloaded, if
it is not already available. If `download_delay` is specified, it should
be the number of seconds that will be paused before initiating a download,
should one be required. If an older version of setuptools is installed,
this routine will print a message to ``sys.stderr`` and raise SystemExit in
an attempt to abort the calling script.
"""
# Work around a hack in the ez_setup.py file from simplejson==1.7.3.
if min_version:
version = min_version
was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
def do_download():
egg = download_setuptools(version, download_base, to_dir, download_delay)
sys.path.insert(0, egg)
import setuptools; setuptools.bootstrap_install_from = egg
try:
import pkg_resources
except ImportError:
return do_download()
try:
pkg_resources.require("setuptools>="+version); return
except pkg_resources.VersionConflict, e:
if was_imported:
print >>sys.stderr, (
"The required version of setuptools (>=%s) is not available, and\n"
"can't be installed while this script is running. Please install\n"
" a more recent version first, using 'easy_install -U setuptools'."
"\n\n(Currently using %r)"
) % (version, e.args[0])
sys.exit(2)
else:
del pkg_resources, sys.modules['pkg_resources'] # reload ok
return do_download()
except pkg_resources.DistributionNotFound:
return do_download()
def download_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
delay = 15
):
"""Download setuptools from a specified location and return its filename
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download attempt.
"""
import urllib2, shutil
egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
url = download_base + egg_name
saveto = os.path.join(to_dir, egg_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
from distutils import log
if delay:
log.warn("""
---------------------------------------------------------------------------
This script requires setuptools version %s to run (even to display
help). I will attempt to download it for you (from
%s), but
you may need to enable firewall access for this script first.
I will start the download in %d seconds.
(Note: if this machine does not have network access, please obtain the file
%s
and place it in this directory before rerunning this script.)
---------------------------------------------------------------------------""",
version, download_base, delay, url
); from time import sleep; sleep(delay)
log.warn("Downloading %s", url)
src = urllib2.urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = _validate_md5(egg_name, src.read())
dst = open(saveto,"wb"); dst.write(data)
finally:
if src: src.close()
if dst: dst.close()
return os.path.realpath(saveto)
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
try:
import setuptools
except ImportError:
egg = None
try:
egg = download_setuptools(version, delay=0)
sys.path.insert(0,egg)
from setuptools.command.easy_install import main
return main(list(argv)+[egg]) # we're done here
finally:
if egg and os.path.exists(egg):
os.unlink(egg)
else:
if setuptools.__version__ == '0.0.1':
print >>sys.stderr, (
"You have an obsolete version of setuptools installed. Please\n"
"remove it from your system entirely before rerunning this script."
)
sys.exit(2)
req = "setuptools>="+version
import pkg_resources
try:
pkg_resources.require(req)
except pkg_resources.VersionConflict:
try:
from setuptools.command.easy_install import main
except ImportError:
from easy_install import main
main(list(argv)+[download_setuptools(delay=0)])
sys.exit(0) # try to force an exit
else:
if argv:
from setuptools.command.easy_install import main
main(argv)
else:
print "Setuptools version",version,"or greater has been installed."
print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
def update_md5(filenames):
"""Update our built-in md5 registry"""
import re
from md5 import md5
for name in filenames:
base = os.path.basename(name)
f = open(name,'rb')
md5_data[base] = md5(f.read()).hexdigest()
f.close()
data = [" %r: %r,\n" % it for it in md5_data.items()]
data.sort()
repl = "".join(data)
import inspect
srcfile = inspect.getsourcefile(sys.modules[__name__])
f = open(srcfile, 'rb'); src = f.read(); f.close()
match = re.search("\nmd5_data = {\n([^}]+)}", src)
if not match:
print >>sys.stderr, "Internal error!"
sys.exit(2)
src = src[:match.start(1)] + repl + src[match.end(1):]
f = open(srcfile,'w')
f.write(src)
f.close()
if __name__=='__main__':
if len(sys.argv)>2 and sys.argv[1]=='--md5update':
update_md5(sys.argv[2:])
else:
main(sys.argv[1:])
lazr.lifecycle-1.0/setup.py 0000755 0001750 0001750 00000004673 11246761210 016205 0 ustar leonardr leonardr #!/usr/bin/env python
# Copyright 2009 Canonical Ltd. All rights reserved.
#
# This file is part of lazr.lifecycle
#
# lazr.lifecycle is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# lazr.lifecycle is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with lazr.lifecycle. If not, see .
import ez_setup
ez_setup.use_setuptools()
import sys
from setuptools import setup, find_packages
# generic helpers primarily for the long_description
def generate(*docname_or_string):
res = []
for value in docname_or_string:
if value.endswith('.txt'):
f = open(value)
value = f.read()
f.close()
res.append(value)
if not value.endswith('\n'):
res.append('')
return '\n'.join(res)
# end generic helpers
__version__ = open("src/lazr/lifecycle/version.txt").read().strip()
setup(
name='lazr.lifecycle',
version=__version__,
namespace_packages=['lazr'],
packages=find_packages('src'),
package_dir={'':'src'},
include_package_data=True,
zip_safe=False,
maintainer='LAZR Developers',
maintainer_email='lazr-developers@lists.launchpad.net',
description=open('README.txt').readline().strip(),
long_description=generate(
'src/lazr/lifecycle/README.txt',
'src/lazr/lifecycle/NEWS.txt'),
license='LGPL v3',
install_requires=[
'setuptools',
'zope.interface',
'zope.component',
'zope.lifecycleevent',
'zope.security'
],
url='https://launchpad.net/lazr.lifecycle',
download_url= 'https://launchpad.net/lazr.yourpkg/+download',
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Operating System :: OS Independent",
"Programming Language :: Python"],
extras_require=dict(
docs=['Sphinx',
'z3c.recipe.sphinxdoc']
),
test_suite='lazr.lifecycle.tests',
)
lazr.lifecycle-1.0/PKG-INFO 0000644 0001750 0001750 00000004607 11246761533 015572 0 ustar leonardr leonardr Metadata-Version: 1.0
Name: lazr.lifecycle
Version: 1.0
Summary: Richer lifecycle events API.
Home-page: https://launchpad.net/lazr.lifecycle
Author: LAZR Developers
Author-email: lazr-developers@lists.launchpad.net
License: LGPL v3
Download-URL: https://launchpad.net/lazr.yourpkg/+download
Description: ..
This file is part of lazr.lifecycle.
lazr.lifecycle is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 3 of the License.
lazr.lifecycle is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with lazr.lifecycle. If not, see .
LAZR lifecycle
**************
This package defines three "lifecycle" events that notify about object
creation, modification and deletion. The events include information about the
user responsible for the changes.
The modification event also includes information about the state of the object
before the changes.
The module also contains Snapshot support to save the state of an object for
notification, and to compute deltas between version of objects.
Other documents
===============
.. toctree::
:glob:
*
docs/*
=======================
NEWS for lazr.lifecycle
=======================
1.0 (2009-08-31)
================
- Remove build dependencies on bzr and egg_info
- remove sys.path hack in setup.py for __version__
0.1 (2009-03-24)
================
- Initial release
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
lazr.lifecycle-1.0/setup.cfg 0000644 0001750 0001750 00000000073 11246761533 016307 0 ustar leonardr leonardr [egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0