lazr.lifecycle-1.0/0000755000175000017500000000000011246761533014466 5ustar leonardrleonardrlazr.lifecycle-1.0/src/0000755000175000017500000000000011246761533015255 5ustar leonardrleonardrlazr.lifecycle-1.0/src/lazr/0000755000175000017500000000000011246761533016225 5ustar leonardrleonardrlazr.lifecycle-1.0/src/lazr/lifecycle/0000755000175000017500000000000011246761533020164 5ustar leonardrleonardrlazr.lifecycle-1.0/src/lazr/lifecycle/docs/0000755000175000017500000000000011246761533021114 5ustar leonardrleonardrlazr.lifecycle-1.0/src/lazr/lifecycle/docs/__init__.py0000644000175000017500000000134511246761142023224 0ustar leonardrleonardr# 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.txt0000644000175000017500000000530611246761142022776 0ustar leonardrleonardrLAZR 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.txt0000644000175000017500000000626311246761142024215 0ustar leonardrleonardrCreating 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.txt0000644000175000017500000001127311246761142023514 0ustar leonardrleonardrGet 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/0000755000175000017500000000000011246761533021326 5ustar leonardrleonardrlazr.lifecycle-1.0/src/lazr/lifecycle/tests/__init__.py0000644000175000017500000000134011246761142023431 0ustar leonardrleonardr# 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.py0000644000175000017500000000437111246761142023670 0ustar leonardrleonardr# 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.txt0000644000175000017500000000040611246761210021471 0ustar leonardrleonardr======================= 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.txt0000644000175000017500000000226411246761142021662 0ustar leonardrleonardr.. 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__.py0000644000175000017500000000146511246761210022273 0ustar leonardrleonardr# 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.py0000644000175000017500000000527011246761142021657 0ustar leonardrleonardr# 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.py0000644000175000017500000000402511246761142022656 0ustar leonardrleonardr# 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.py0000644000175000017500000000504511246761142023016 0ustar leonardrleonardr# 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.py0000644000175000017500000000560711246761142022401 0ustar leonardrleonardr# 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.txt0000644000175000017500000000000411246761210022374 0ustar leonardrleonardr1.0 lazr.lifecycle-1.0/src/lazr/__init__.py0000644000175000017500000000161711246761142020337 0ustar leonardrleonardr# 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/0000755000175000017500000000000011246761533021655 5ustar leonardrleonardrlazr.lifecycle-1.0/src/lazr.lifecycle.egg-info/PKG-INFO0000644000175000017500000000460711246761533022761 0ustar leonardrleonardrMetadata-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.txt0000644000175000017500000000146611246761533023550 0ustar leonardrleonardrREADME.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.pylazr.lifecycle-1.0/src/lazr.lifecycle.egg-info/dependency_links.txt0000644000175000017500000000000111246761533025723 0ustar leonardrleonardr lazr.lifecycle-1.0/src/lazr.lifecycle.egg-info/namespace_packages.txt0000644000175000017500000000000511246761533026203 0ustar leonardrleonardrlazr lazr.lifecycle-1.0/src/lazr.lifecycle.egg-info/not-zip-safe0000644000175000017500000000000111246761533024103 0ustar leonardrleonardr lazr.lifecycle-1.0/src/lazr.lifecycle.egg-info/requires.txt0000644000175000017500000000015611246761533024257 0ustar leonardrleonardrsetuptools zope.interface zope.component zope.lifecycleevent zope.security [docs] Sphinx z3c.recipe.sphinxdoclazr.lifecycle-1.0/src/lazr.lifecycle.egg-info/top_level.txt0000644000175000017500000000000511246761533024402 0ustar leonardrleonardrlazr lazr.lifecycle-1.0/README.txt0000644000175000017500000000130011246761142016152 0ustar leonardrleonardrRicher 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.py0000644000175000017500000002246611246761142016704 0ustar leonardrleonardr#!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.py0000755000175000017500000000467311246761210016205 0ustar leonardrleonardr#!/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-INFO0000644000175000017500000000460711246761533015572 0ustar leonardrleonardrMetadata-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.cfg0000644000175000017500000000007311246761533016307 0ustar leonardrleonardr[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0