Function
zope.app.container.contained.setitem

Signature

setitem(container, setitemf, name, object)

Documentation String

Helper function to set an item and generate needed events

This helper is needed, in part, because the events need to get published after the object has been added to the container.

If the item implements IContained, simply set its __parent__ and __name__ attributes:

>>> class IItem(zope.interface.Interface):
...     pass
>>> class Item(Contained):
...     zope.interface.implements(IItem)
...     def setAdded(self, event):
...         self.added = event
...     def setMoved(self, event):
...         self.moved = event
>>> from zope.app.container.interfaces import IObjectAddedEvent
>>> from zope.app.container.interfaces import IObjectMovedEvent
>>> from zope.app.testing import ztapi
>>> ztapi.subscribe([IItem, IObjectAddedEvent], None,
...                 lambda obj, event: obj.setAdded(event))
>>> ztapi.subscribe([IItem, IObjectMovedEvent], None,
...                 lambda obj, event: obj.setMoved(event))
>>> item = Item()
>>> container = {}
>>> setitem(container, container.__setitem__, u'c', item)
>>> container[u'c'] is item
1
>>> item.__parent__ is container
1
>>> item.__name__
u'c'

If we run this using the testing framework, we'll use getEvents to track the events generated:

>>> from zope.component.eventtesting import getEvents
>>> from zope.lifecycleevent.interfaces import IObjectModifiedEvent

We have an added event:

>>> len(getEvents(IObjectAddedEvent))
1
>>> event = getEvents(IObjectAddedEvent)[-1]
>>> event.object is item
1
>>> event.newParent is container
1
>>> event.newName
u'c'
>>> event.oldParent
>>> event.oldName

As well as a modification event for the container:

>>> len(getEvents(IObjectModifiedEvent))
1
>>> getEvents(IObjectModifiedEvent)[-1].object is container
1

The item's hooks have been called:

>>> item.added is event
1
>>> item.moved is event
1

We can suppress events and hooks by setting the __parent__ and __name__ first:

>>> item = Item()
>>> item.__parent__, item.__name__ = container, 'c2'
>>> setitem(container, container.__setitem__, u'c2', item)
>>> len(container)
2
>>> len(getEvents(IObjectAddedEvent))
1
>>> len(getEvents(IObjectModifiedEvent))
1
>>> getattr(item, 'added', None)
>>> getattr(item, 'moved', None)

If the item had a parent or name (as in a move or rename), we generate a move event, rather than an add event:

>>> setitem(container, container.__setitem__, u'c3', item)
>>> len(container)
3
>>> len(getEvents(IObjectAddedEvent))
1
>>> len(getEvents(IObjectModifiedEvent))
2
>>> len(getEvents(IObjectMovedEvent))
2

(Note that we have 2 move events because add are move events.)

We also get the move hook called, but not the add hook:

>>> event = getEvents(IObjectMovedEvent)[-1]
>>> getattr(item, 'added', None)
>>> item.moved is event
1

If we try to replace an item without deleting it first, we'll get an error:

>>> setitem(container, container.__setitem__, u'c', [])
Traceback (most recent call last):
...
DuplicationError: c
>>> del container[u'c']
>>> setitem(container, container.__setitem__, u'c', [])
>>> len(getEvents(IObjectAddedEvent))
2
>>> len(getEvents(IObjectModifiedEvent))
3

If the object implements ILocation, but not IContained, set it's __parent__ and __name__ attributes and declare that it implements IContained:

>>> from zope.location import Location
>>> item = Location()
>>> IContained.providedBy(item)
0
>>> setitem(container, container.__setitem__, u'l', item)
>>> container[u'l'] is item
1
>>> item.__parent__ is container
1
>>> item.__name__
u'l'
>>> IContained.providedBy(item)
1

We get new added and modification events:

>>> len(getEvents(IObjectAddedEvent))
3
>>> len(getEvents(IObjectModifiedEvent))
4

If the object doesn't even implement ILocation, put a ContainedProxy around it:

>>> item = []
>>> setitem(container, container.__setitem__, u'i', item)
>>> container[u'i']
[]
>>> container[u'i'] is item
0
>>> item = container[u'i']
>>> item.__parent__ is container
1
>>> item.__name__
u'i'
>>> IContained.providedBy(item)
1
>>> len(getEvents(IObjectAddedEvent))
4
>>> len(getEvents(IObjectModifiedEvent))
5

We'll get type errors if we give keys that aren't unicode or ascii keys:

>>> setitem(container, container.__setitem__, 42, item)
Traceback (most recent call last):
...
TypeError: name not unicode or ascii string
>>> setitem(container, container.__setitem__, None, item)
Traceback (most recent call last):
...
TypeError: name not unicode or ascii string
>>> setitem(container, container.__setitem__, 'hello ' + chr(200), item)
Traceback (most recent call last):
...
TypeError: name not unicode or ascii string

and we'll get a value error of we give an empty string or unicode:

>>> setitem(container, container.__setitem__, '', item)
Traceback (most recent call last):
...
ValueError: empty names are not allowed
>>> setitem(container, container.__setitem__, u'', item)
Traceback (most recent call last):
...
ValueError: empty names are not allowed