What is zope.lifecycleevent?¶
zope.lifecycleevent
¶
Overview¶
In a loosely-coupled system, events can be used by parts of the system to inform each other about relevant occurrences. The zope.event package (optionally together with zope.interface and zope.component) provides a generic mechanism to dispatch objects representing those events to interested subscribers (e.g., functions). This package defines a specific set of event objects and API functions for describing the life-cycle of objects in the system: object creation, object modification, and object removal.
Documentation is hosted at https://zopelifecycleevent.readthedocs.io
Quick Start¶
This document describes the various event types defined by this package and provides some basic examples of using them to inform parts of the system about object changes.
All events have three components: an interface defining the event’s
structure, a default implementation of that interface (the event
object), and a high-level convenience function (defined by the
IZopeLifecycleEvent
interface) for easily sending that
event in a single function call.
Note
The convenience functions are simple wrappers for
constructing an event object and sending it via
zope.event.notify()
. Here we will only discuss using these
functions; for more information on the advanced usage of when and
how to construct and send event objects manually, see
Creating and Sending Events.
Note
This document will not discuss actually handling these events (setting up subscribers for them). For information on that topic, see Handling Events.
We will go through the events in approximate order of how they would be used to follow the life-cycle of an object.
Creation¶
The first event is IObjectCreatedEvent
, implemented by
ObjectCreatedEvent
, which is used to communicate that a single object
has been created. It can be sent with the
zope.lifecycleevent.created()
function.
For example:
>>> from zope.lifecycleevent import created
>>> obj = {}
>>> created(obj)
Copying¶
Copying an object is a special case of creating one. It can happen at
any time and is implemented with IObjectCopiedEvent
,
ObjectCopiedEvent
, or the API
zope.lifecycleevent.copied()
.
>>> from zope.lifecycleevent import copied
>>> import pickle
>>> copy = pickle.loads(pickle.dumps(obj))
>>> copied(copy, obj)
Note
Handlers for IObjectCreatedEvent
can expect to
receive events for IObjectCopiedEvent
as well.
Addition¶
After objects are created, it is common to add them somewhere for
storage or access. This can be accomplished with the
IObjectAddedEvent
and its implementation
ObjectAddedEvent
, or the API
zope.lifecycleevent.added()
.
>>> from zope.lifecycleevent import ObjectAddedEvent
>>> from zope.lifecycleevent import added
>>> container = {}
>>> container['name'] = obj
>>> added(obj, container, 'name')
If the object being added has a non-None __name__
or __parent__
attribute, we can omit those values when we call added
and the
attributes will be used.
>>> class Location(object):
... __parent__ = None
... __name__ = None
>>> location = Location()
>>> location.__name__ = "location"
>>> location.__parent__ = container
>>> container[location.__name__] = location
>>> added(location)
Tip
The interface zope.location.interfaces.ILocation
defines these attributes (although we don’t require the object to
implement that interface), and containers that implement
zope.container.interfaces.IWriteContainer
are expected to
set them (such containers will also automatically send the
IObjectAddedEvent
).
Modification¶
One of the most common types of events used from this package is the
IObjectModifiedEvent
(implemented by
ObjectModifiedEvent
) that represents object modification.
In the simplest case, it may be enough to simply notify interested
parties that the object has changed. Like the other events, this can
be done manually or through the convenience API
(zope.lifecycleevent.modified()
):
>>> obj['key'] = 42
>>> from zope.lifecycleevent import modified
>>> modified(obj)
Providing Additional Information¶
Some event consumers like indexes (catalogs) and caches may need more
information to update themselves in an efficient manner. The necessary
information can be provided as optional “modification descriptions” of
the ObjectModifiedEvent
(or again, via the
modified()
function).
This package doesn’t strictly define what a “modification description” must be. The most common (and thus most interoperable) descriptions are based on interfaces.
We could simply pass an interface itself to say “something about the way this object implements the interface changed”:
>>> from zope.interface import Interface, Attribute, implementer
>>> class IFile(Interface):
... data = Attribute("The data of the file.")
... name = Attribute("The name of the file.")
>>> @implementer(IFile)
... class File(object):
... data = ''
... name = ''
>>> file = File()
>>> created(file)
>>> file.data = "123"
>>> modified(file, IFile)
Attributes¶
We can also be more specific in a case like this where we know exactly
what attribute of the interface we modified. There is a helper class
zope.lifecycleevent.Attributes
that assists:
>>> from zope.lifecycleevent import Attributes
>>> file.data = "abc"
>>> modified(file, Attributes(IFile, "data"))
If we modify multiple attributes of an interface at the same time, we
can include that information in a single Attributes
object:
>>> file.data = "123"
>>> file.name = "123.txt"
>>> modified(file, Attributes(IFile, "data", "name"))
Sometimes we may change attributes from multiple interfaces at the
same time. We can also represent this by including more than one
Attributes
instance:
>>> import time
>>> class IModified(Interface):
... lastModified = Attribute("The timestamp when the object was modified.")
>>> @implementer(IModified)
... class ModifiedFile(File):
... lastModified = 0
>>> file = ModifiedFile()
>>> created(file)
>>> file.data = "abc"
>>> file.lastModified = time.time()
>>> modified(file,
... Attributes(IFile, "data"),
... Attributes(IModified, "lastModified"))
Sequences¶
When an object is a sequence or container, we can specify
the individual indexes or keys that we changed using
zope.lifecycleevent.Sequence
.
First we’ll need to define a sequence and create an instance:
>>> from zope.interface.common.sequence import ISequence
>>> class IFileList(ISequence):
... "A sequence of IFile objects."
>>> @implementer(IFileList)
... class FileList(list):
... pass
>>> files = FileList()
>>> created(files)
Now we can modify the sequence by adding an object to it:
>>> files.append(File())
>>> from zope.lifecycleevent import Sequence
>>> modified(files, Sequence(IFileList, len(files) - 1))
We can also replace an existing object:
>>> files[0] = File()
>>> modified(files, Sequence(IFileList, 0))
Of course Attributes
and Sequences
can be combined in any
order and length necessary to describe the modifications fully.
Modification Descriptions¶
Although this package does not require any particular definition or
implementation of modification descriptions, it provides the two that
we’ve already seen: Attributes
and
Sequence
. Both of these classes
implement the marker interface
IModificationDescription
. If
you implement custom modification descriptions, consider implementing
this marker interface.
Movement¶
Sometimes objects move from one place to another. This can be
described with the interface IObjectMovedEvent
, its
implementation ObjectMovedEvent
or the API
zope.lifecycleevent.moved()
.
Objects may move within a single container by changing their name:
>>> from zope.lifecycleevent import moved
>>> container['new name'] = obj
>>> del container['name']
>>> moved(obj,
... oldParent=container, oldName='name',
... newParent=container, newName='new name')
Or they may move to a new container (under the same name, or a different name):
>>> container2 = {}
>>> container2['new name'] = obj
>>> del container['new name']
>>> moved(obj,
... oldParent=container, oldName='new name',
... newParent=container2, newName='new name')
Unlike addition, any __name__
and __parent__
attribute on the object are ignored and must be provided explicitly.
Tip
Much like the addition of objects,
zope.container.interfaces.IWriteContainer
implementations
are expected to update the __name__
and __parent__
attributes automatically, and to automatically send the appropriate
movement event.
Removal¶
Finally, objects can be removed from the system altogether with
IObjectRemovedEvent
, ObjectRemovedEvent
and
zope.lifecycleevent.removed()
.
>>> from zope.lifecycleevent import removed
>>> del container2['new name']
>>> removed(obj, container2, 'new name')
Note
This is a special case of movement where the new parent and
new name are always None
. Handlers for
IObjectMovedEvent
can expect to receive events for
IObjectRemovedEvent
as well.
If the object being removed provides the __name__
or
__parent__
attribute, those arguments can be omitted and the
attributes will be used instead.
>>> location = container['location']
>>> del container[location.__name__]
>>> removed(location)
Tip
Once again, IWriteContainer
implementations will send the correct event automatically.
Creating and Sending Events¶
As discussed in Quick Start, most uses of
zope.lifecycleevent
will be satisfied with the high level API
described by
IZopeLifecycleEvent
, but it is
possible to create and send events manually, both those defined here
and your own subclasses.
Provided Events¶
All of the functions described in Quick Start are very simple
wrappers that create an event object defined by this package and then
use zope.event.notify()
to send it. You can do the same, as
shown below, but there is usually little reason to do so.
>>> from zope.event import notify
>>> from zope.lifecycleevent import ObjectCreatedEvent
>>> from zope.lifecycleevent import ObjectCopiedEvent
>>> from zope.lifecycleevent import ObjectModifiedEvent
>>> from zope.lifecycleevent import ObjectMovedEvent
>>> from zope.lifecycleevent import ObjectRemovedEvent
>>> obj = object()
>>> notify(ObjectCreatedEvent(obj))
>>> notify(ObjectCopiedEvent(object(), obj))
>>> notify(ObjectMovedEvent(obj,
... None, 'oldName',
... None, 'newName'))
>>> notify(ObjectModifiedEvent(obj, "description 1", "description 2"))
>>> notify(ObjectRemovedEvent(obj, "oldParent", "oldName"))
Subclassing Events¶
It can sometimes be helpful to subclass one of the provided event classes. If you then want to send a notification of that class, you must manually construct and notify it.
One reason to create a subclass is to be able to add additional
attributes to the event object, perhaps changing the constructor
signature in the process. Another reason to create a subclass is to be
able to easily subscribe to all events that are just of that class.
The class zope.container.contained.ContainerModifiedEvent
is
used for this reason.
For example, in an application with distinct users, we might want to let subscribers know which user created the object. We might also want to be able to distinguish between objects that are created by a user and those that are automatically created as part of system operation or administration. The following subclass lets us do both.
>>> class ObjectCreatedByEvent(ObjectCreatedEvent):
... "A created event that tells you who created the object."
... def __init__(self, object, created_by):
... super(ObjectCreatedByEvent, self).__init__(object)
... self.created_by = created_by
>>> obj = object()
>>> notify(ObjectCreatedByEvent(obj, "Black Night"))
Handling Events¶
This document provides information on how to handle the lifycycle events defined and sent by this package.
Background information on handling events is found in
zope.event's documentation
.
Class Based Handling¶
zope.event
includes a simple framework for dispatching
events based on the class of the event. This could be used to provide
handlers for each of the event classes defined by this package
(ObjectCreatedEvent
, etc). However, it doesn’t allow
configuring handlers based on the kind of object the event contains.
To do that, we need another level of dispatching.
Fortunately, that level of dispatching already exists within
zope.component
.
Component Based Handling¶
zope.component
includes an event dispatching framework that
lets us dispatch events based not just on the kind of the event, but
also on the kind of object the event contains.
All of the events defined by this package are implementations of
zope.interface.interfaces.IObjectEvent
. zope.component
includes special support for these kinds of events. That document
walks through a generic example in Python code. Here we will show an
example specific to life cycle events using the type of configuration
that is more likely to be used in a real application.
For this to work, it’s important that zope.component
is configured
correctly. Usually this is done with ZCML executed at startup time (we
will be using strings in this documentation, but usually this resides
in files, most often named configure.zcml
):
>>> from zope.configuration import xmlconfig
>>> _ = xmlconfig.string("""
... <configure xmlns="http://namespaces.zope.org/zope">
... <include package="zope.component" />
... </configure>
... """)
First we will define an object we’re interested in getting events for:
>>> from zope.interface import Interface, Attribute, implementer
>>> class IFile(Interface):
... data = Attribute("The data of the file.")
... name = Attribute("The name of the file.")
>>> @implementer(IFile)
... class File(object):
... data = ''
... name = ''
Next, we will write our subscriber. Normally, zope.event
subscribers take just one argument, the event object. But when we use
the automatic dispatching that zope.component
provides, our
function will receive two arguments: the object of the event, and
the event. We can use the decorators that zope.component
supplies
to annotate the function with the kinds of arguments it wants to
handle. Alternatively, we could specify that information when we
register the handler with zope.component (we’ll see an example of that
later).
>>> from zope.component import adapter
>>> from zope.lifecycleevent import IObjectCreatedEvent
>>> @adapter(IFile, IObjectCreatedEvent)
... def on_file_created(file, event):
... print("A file of type '%s' was created" % (file.__class__.__name__))
Finally, we will register our handler with zope.component. This is also usually done with ZCML executed at startup time:
>>> _ = xmlconfig.string("""
... <configure xmlns="http://namespaces.zope.org/zope">
... <include package="zope.component" file="meta.zcml" />
... <subscriber handler="__main__.on_file_created"/>
... </configure>
... """)
Now we can send an event noting that a file was created, and our handler will be called:
>>> from zope.lifecycleevent import created
>>> file = File()
>>> created(file)
A file of type 'File' was created
Other types of objects don’t trigger our handler:
>>> created(object)
The hierarchy is respected, so if we define a subclass of File
and
indeed, even a sub-interface of IFile
, our handler will be
invoked.
>>> class SubFile(File): pass
>>> created(SubFile())
A file of type 'SubFile' was created
>>> class ISubFile(IFile): pass
>>> @implementer(ISubFile)
... class IndependentSubFile(object):
... data = name = ''
>>> created(IndependentSubFile())
A file of type 'IndependentSubFile' was created
We can further register a handler just for the subinterface we created. Here we’ll also demonstrate supplying this information in ZCML.
>>> def generic_object_event(obj, event):
... print("Got '%s' for an object of type '%s'" % (event.__class__.__name__, obj.__class__.__name__))
>>> _ = xmlconfig.string("""
... <configure xmlns="http://namespaces.zope.org/zope">
... <include package="zope.component" file="meta.zcml" />
... <subscriber handler="__main__.generic_object_event"
... for="__main__.ISubFile zope.lifecycleevent.IObjectCreatedEvent" />
... </configure>
... """)
Now both handlers will be called for implementations of ISubFile
,
but still only the original implementation will be called for base IFiles
.
>>> created(IndependentSubFile())
A file of type 'IndependentSubFile' was created
Got 'ObjectCreatedEvent' for an object of type 'IndependentSubFile'
>>> created(File())
A file of type 'File' was created
Projects That Rely on Dispatched Events¶
Handlers for life cycle events are commonly registered with
zope.component
as a means for keeping projects uncoupled. This
section provides a partial list of such projects for reference.
As mentioned in Quick Start, the containers provided by zope.container generally automatically send the correct life cycle events.
At a low-level, there are utilities that assign integer IDs to objects
as they are created such as zope.intid and zc.intid.
zc.intid
, in particular, documents the way it uses events.
zope.catalog
can automatically index documents as part of
handling life cycle events.
Containers and Sublocations¶
The events ObjectAddedEvent
and ObjectRemovedEvent
usually need to be (eventually) sent in pairs for any given object.
That is, when an added event is sent for an object, for symmetry
eventually a removed event should be sent too. This makes sure that
proper cleanup can happen.
Sometimes one object can be said to contain other objects. This is obvious in the case of lists, dictionaries and the container objects provided by zope.container, but the same can sometimes be said for other types of objects too that reference objects in their own attributes.
What happens when a life cycle event for such an object is sent? By default, nothing. This may leave the system in an inconsistent state.
For example, lets create a container and add some objects to it. First we’ll set up a generic event handler so we can see the events that go out.
>>> _ = xmlconfig.string("""
... <configure xmlns="http://namespaces.zope.org/zope">
... <include package="zope.component" file="meta.zcml" />
... <subscriber handler="__main__.generic_object_event"
... for="* zope.interface.interfaces.IObjectEvent" />
... </configure>
... """)
Got...
>>> from zope.lifecycleevent import added
>>> container = {}
>>> created(container)
Got 'ObjectCreatedEvent' for an object of type 'dict'
>>> object1 = object()
>>> container['object1'] = object1
>>> added(object1, container, 'object1')
Got 'ObjectAddedEvent' for an object of type 'object'
We can see that we got an “added” event for the object we stored in the container. What happens when we remove the container?
>>> from zope.lifecycleevent import removed
>>> tmp = container
>>> del container
>>> removed(tmp, '', '')
Got 'ObjectRemovedEvent' for an object of type 'dict'
>>> del tmp
We only got an event for the container, not the objects it contained!
If the handlers that fired when we added “object1” had done anything
that needed to be undone for symmetry when “object1” was removed
(e.g., if it had been indexed and needed to be unindexed) the system
is now corrupt because those handlers never got the
ObjectRemovedEvent
for “object1”.
The solution to this problem comes from zope.container. It defines
the concept of ISubLocations
: a
way for any given object to inform other objects about the objects it
contains (and it provides a default implementation of
ISubLocations
for
containers). It also provides a function
that will send
events that happen to the parent object for all the child objects
it contains.
In this way, its possible for any arbitrary life cycle event to
automatically be propagated to its children without any specific
caller of remove
, say, needing to have any specific knowledge
about containment relationships.
For this to work, two things must be done:
- Configure zope.container. This too is usually done in ZCML with
<include package="zope.container"/>
. - Provide an adapter to
ISubLocations
when some object can contain other objects that need events.
Reference¶
Interfaces¶
Event-related interfaces
-
interface
zope.lifecycleevent.interfaces.
IZopeLifecycleEvent
[source]¶ High-level functions for sending events.
These are implemented by the
zope.lifecycleevent
module.-
created
(object)¶ Send an
IObjectCreatedEvent
forobject
.
-
modified
(object, *descriptions)¶ Send an
IObjectModifiedEvent
forobject
.descriptions
is a sequence of interfaces or fields which were updated. TheIAttributes
andISequence
helpers can be used.
-
copied
(object, original)¶ Send an
IObjectCopiedEvent
forobject
.original
is the object the copy was created from.
-
moved
(object, oldParent, oldName, newParent, newName)¶ Send an
IObjectMovedEvent
forobject
.oldParent
is the containerobject
was removed from.oldName
was the name used to storeobject
inoldParent
.newParent
is the containerobject
was added to.newName
is the name used to storeobject
innewParent
.Note that
newParent
andoldParent
may be the same if the names are different, and vice versa.
-
added
(object, newParent=None, newName=None)¶ Send an
IObjectAddedEvent
forobject
.newParent
is the containerobject
was added to.newName
is the name used to storeobject
in the container.If either of these is not provided or is
None
, they will be taken from the values ofobject.__parent__
orobject.__name__
, respectively.
-
removed
(object, oldParent=None, oldName=None)¶ Send an
IObjectRemovedEvent
forobject
.oldParent
is the containerobject
was removed from.oldName
was the name used to storeobject
in oldParent.If either of these is not provided or is
None
, they will be taken from the values ofobject.__parent__
orobject.__name__
, respectively.
-
-
interface
zope.lifecycleevent.interfaces.
IObjectCreatedEvent
[source]¶ Extends:
zope.interface.interfaces.IObjectEvent
An object has been created.
The
object
attribute will commonly have a value ofNone
for its__name__
and__parent__
values (if it has those attributes at all).
-
interface
zope.lifecycleevent.interfaces.
IObjectCopiedEvent
[source]¶ Extends:
zope.lifecycleevent.interfaces.IObjectCreatedEvent
An object has been copied.
-
original
¶ The original from which the copy was made.
-
-
interface
zope.lifecycleevent.interfaces.
IObjectModifiedEvent
[source]¶ Extends:
zope.interface.interfaces.IObjectEvent
An object has been modified
-
descriptions
¶ The supplied modification descriptions.
These may be interfaces or implementations of
IModificationDescription
such asAttributes
orSequence
-
-
interface
zope.lifecycleevent.interfaces.
IModificationDescription
[source]¶ Marker interface for descriptions of object modifications.
Can be used as a parameter of an IObjectModifiedEvent.
-
interface
zope.lifecycleevent.interfaces.
IAttributes
[source]¶ Extends:
zope.lifecycleevent.interfaces.IModificationDescription
Describes the attributes of an interface.
-
interface
¶ The involved interface.
-
attributes
¶ A sequence of modified attributes.
-
-
interface
zope.lifecycleevent.interfaces.
ISequence
[source]¶ Extends:
zope.lifecycleevent.interfaces.IModificationDescription
Describes the modified keys of a sequence-like interface.
-
interface
¶ The involved interface.
-
keys
¶ A sequence of modified keys.
-
-
interface
zope.lifecycleevent.interfaces.
IObjectMovedEvent
[source]¶ Extends:
zope.interface.interfaces.IObjectEvent
An object has been moved.
-
oldParent
¶ The old location parent for the object.
-
oldName
¶ The old location name for the object.
-
newParent
¶ The new location parent for the object.
-
newName
¶ The new location name for the object.
-
-
interface
zope.lifecycleevent.interfaces.
IObjectAddedEvent
[source]¶ Extends:
zope.lifecycleevent.interfaces.IObjectMovedEvent
An object has been added to a container.
-
interface
zope.lifecycleevent.interfaces.
IObjectRemovedEvent
[source]¶ Extends:
zope.lifecycleevent.interfaces.IObjectMovedEvent
An object has been removed from a container.
Implementation¶
Life cycle events.
This module provides the IZopeLifecycleEvent
interface,
in addition to concrete classes implementing the various event interfaces.
-
class
zope.lifecycleevent.
ObjectCreatedEvent
(object)[source]¶ Bases:
zope.interface.interfaces.ObjectEvent
An object has been created
-
class
zope.lifecycleevent.
Attributes
(interface, *attributes)[source]¶ Describes modified attributes of an interface.
-
class
zope.lifecycleevent.
Sequence
(interface, *keys)[source]¶ Describes modified keys of an interface.
-
class
zope.lifecycleevent.
ObjectModifiedEvent
(object, *descriptions)[source]¶ Bases:
zope.interface.interfaces.ObjectEvent
An object has been modified
Init with a list of modification descriptions.
-
zope.lifecycleevent.
modified
(object, *descriptions)[source]¶ Send an
IObjectModifiedEvent
forobject
.descriptions
is a sequence of interfaces or fields which were updated. TheIAttributes
andISequence
helpers can be used.
-
class
zope.lifecycleevent.
ObjectCopiedEvent
(object, original)[source]¶ Bases:
zope.lifecycleevent.ObjectCreatedEvent
An object has been copied
-
zope.lifecycleevent.
copied
(object, original)[source]¶ Send an
IObjectCopiedEvent
forobject
.original
is the object the copy was created from.
-
class
zope.lifecycleevent.
ObjectMovedEvent
(object, oldParent, oldName, newParent, newName)[source]¶ Bases:
zope.interface.interfaces.ObjectEvent
An object has been moved
-
zope.lifecycleevent.
moved
(object, oldParent, oldName, newParent, newName)[source]¶ Send an
IObjectMovedEvent
forobject
.oldParent
is the containerobject
was removed from.oldName
was the name used to storeobject
inoldParent
.newParent
is the containerobject
was added to.newName
is the name used to storeobject
innewParent
.Note that
newParent
andoldParent
may be the same if the names are different, and vice versa.
-
class
zope.lifecycleevent.
ObjectAddedEvent
(object, newParent=None, newName=None)[source]¶ Bases:
zope.lifecycleevent.ObjectMovedEvent
An object has been added to a container.
If
newParent
ornewName
is not provided or isNone
, they will be taken from the values ofobject.__parent__
orobject.__name__
, respectively.
-
zope.lifecycleevent.
added
(object, newParent=None, newName=None)[source]¶ Send an
IObjectAddedEvent
forobject
.newParent
is the containerobject
was added to.newName
is the name used to storeobject
in the container.If either of these is not provided or is
None
, they will be taken from the values ofobject.__parent__
orobject.__name__
, respectively.
-
class
zope.lifecycleevent.
ObjectRemovedEvent
(object, oldParent=None, oldName=None)[source]¶ Bases:
zope.lifecycleevent.ObjectMovedEvent
An object has been removed from a container.
If
oldParent
oroldName
is not provided or isNone
, they will be taken from the values ofobject.__parent__
orobject.__name__
, respectively.
-
zope.lifecycleevent.
removed
(object, oldParent=None, oldName=None)[source]¶ Send an
IObjectRemovedEvent
forobject
.oldParent
is the containerobject
was removed from.oldName
was the name used to storeobject
in oldParent.If either of these is not provided or is
None
, they will be taken from the values ofobject.__parent__
orobject.__name__
, respectively.
Changes¶
5.0 (2023-07-06)¶
- Drop support for Python 2.7, 3.5, 3.6.
- Add support for Python 3.11.
4.4 (2022-05-09)¶
- Add support for Python 3.8, 3,9, 3.10.
- Drop support for Python 3.4.
4.3 (2018-10-05)¶
- Add support for Python 3.7.
4.2.0 (2017-07-12)¶
- Add support for Python 3.5 and 3.6.
- Drop support for Python 2.6 and 3.3.
- Documentation is hosted at https://zopelifecycleevent.readthedocs.io
4.1.0 (2014-12-27)¶
- Add support for PyPy3.
- Add support for Python 3.4.
4.0.3 (2013-09-12)¶
- Drop the dependency on
zope.component
as the interface and implementation ofObjectEvent
is now inzope.interface
. Retained the dependency for the tests. - Fix:
.moved
tried to notify the wrong event.
4.0.2 (2013-03-08)¶
- Add Trove classifiers indicating CPython and PyPy support.
4.0.1 (2013-02-11)¶
- Add tox.ini.
4.0.0 (2013-02-11)¶
- Test coverage at 100%.
- Add support for Python 3.3 and PyPy.
- Replace deprecated
zope.interface.implements
usage with equivalentzope.interface.implementer
decorator. - Drop support for Python 2.4 and 2.5.
3.7.0 (2011-03-17)¶
- Add convenience functions to parallel
zope.lifecycleevent.modified
for the other events defined in this package.
3.6.2 (2010-09-25)¶
- Add not declared, but needed test dependency on
zope.component [test]
.
3.6.1 (2010-04-30)¶
- Remove dependency on undeclared
zope.testing.doctest
.
3.6.0 (2009-12-29)¶
- Refactor tests to lose
zope.annotation
andzope.dublincore
as dependencies.
3.5.2 (2009-05-17)¶
- Copy
IObjectMovedEvent
,IObjectAddedEvent
,IObjectRemovedEvent
interfaces andObjectMovedEvent
,ObjectAddedEvent
andObjectRemovedEvent
classes here fromzope.container
(plus tests). The intent is to allow packages that rely on these interfaces or the event classes to rely onzope.lifecycleevent
(which has few dependencies) instead ofzope.container
(which has many).
3.5.1 (2009-03-09)¶
- Remove deprecated code and therefore dependency on
zope.deferredimport
. - Change package’s mailing list address to zope-dev at zope.org, as zope3-dev at zope.org is now retired.
- Update package’s description and documentation.
3.5.0 (2009-01-31)¶
- Remove old module declarations from classes.
- Use
zope.container
instead ofzope.app.container
.
3.4.0 (2007-09-01)¶
Initial release as an independent package
Project URLs¶
- https://pypi.python.org/pypi/zope.lifecycleevent (PyPI entry and downloads)