[Zope3-dev] Re: proposed changes to contained helper functions

Garrett Smith garrett at mojave-corp.com
Thu Oct 2 14:11:12 EDT 2003


Just a quick followup to this...I noticed one area in the existing Zope 
source that would benefit from change 1 below:

Current code in zope/app/services/servicecontainer.py, lines 45ff:

     def setSiteManager(self, sm):
         ...
         if IServiceService.isImplementedBy(sm):
             self.__sm = sm
             sm.__name__ = '++etc++site'
             sm.__parent__ = self
         else:
             raise ValueError(...)
         ...

would become:

     def setSiteManager(self, sm):
         ...
         if IServiceService.isImplementedBy(sm):
             self.__sm = containedObject(sm, self, '++etc++site')
         else:
             raise ValueError(...)
         ...

While this isn't a revolution ;-) it's the type of explicit syntax that 
I'd expect in something as central as container/contained relationships 
in Zope.

  -- Garrett


Garrett Smith wrote:

> This is a mini-proposal based on my experience in refactoring for 
> containergeddon.
> 
> My overall feedback is that the geddon is a *huge* improvement in the 
> maintainability of container/contained relationships. I'm delighted with 
> the prospect of never seeing the word 'wrapper' in our code again :)
> 
> However, the current set of helper functions (setitem, containedEvent, 
> uncontained) are very specific to containers. While this may make sense 
> for most applications, I have a couple of use cases that fall outside 
> the current design:
> 
> 1. I need to setup parent/child (i.e. container/contained) relationships 
> for object attributes:
> 
>   class Foo:
>       def __init__(self):
>           self.bar = Bar()  # bar is a child of a Foo object
> 
> In this case, I'm interested in setting up parent/child relationships, 
> but not generating events. I would be inclined to use containedEvent for 
> this case, but it creates an event and its semantics are wrong.
> 
> 2. I have a class that uses a container internally to store objects, but 
> is itself not a container:
> 
>   class Foo:
>       def __init__(self):
>           self._data = {}
> 
>       def addBar(self, name, bar):
>           self._data[name] = bar  # bar is a child a Foo object
> 
>       # other bar related methods, e.g. getBars, containsBar, etc.
> 
> In this case, I'm interested in setting up parent/child relationships 
> *and* generating events. I would be inclined to use setitem, but setitem 
> assumes that the parent implements IContainer, which in this case false.
> 
> Below are my proposed changes:
> 
> 1. Add a new helper function 'containedObject'
> 
>  def containedObject(object, parent, name=None):
>      if not IContained.isImplementedBy(object):
>          if ILocation.isImplementedBy(object):
>              zope.interface.directlyProvides(object, IContained)
>          else:
>              object = ContainedProxy(object)
> 
>      object.__parent__ = parent
>      object.__name__ = name
> 
>      return object
> 
> The class Foo in case 1 would be as follows:
> 
>   class Foo:
>       def __init__(self):
>           self.bar = containedObject(Bar(), self, 'bar')
> 
> 2. Add a new helper function 'setContained':
> 
>  def setContained(parent, setf, getf, name, object):
> 
>      # Do basic name check:
>      if isinstance(name, str):
>          try:
>              name = unicode(name)
>          except UnicodeError:
>              raise TypeError("name not unicode or ascii string")
>      elif not isinstance(name, unicode):
>          raise TypeError("name not unicode or ascii string")
> 
>      if not name:
>          raise ValueError("empty names are not allowed")
> 
>      old = getf(name)
>      if old is object:
>          return
>      if old is not None:
>          raise DuplicationError(name)
> 
>      object, event = containedEvent(object, parent, name)
>      setf(name, object)
>      if event:
>          if event.__class__ is ObjectAddedEvent:
>              a = zapi.queryAdapter(object, IAddNotifiable)
>              if a is not None:
>                  a.addNotify(event)
>          a = zapi.queryAdapter(object, IMoveNotifiable)
>          if a is not None:
>              a.moveNotify(event)
>          publish(container, event)
>          modified(container)
> 
> This would allow Foo's addBar method to be as follows:
> 
>  def addBar(self, name, bar):
>      setContained(
>          parent=self,
>          setf=self._data.__setitem__,
>          getf=self._data.get,
>          name=name,
>          object=bar)
> 
> This function is identical to setitem with the exception that it deals 
> with a generic parent rather than a container.
> 
> This function would allow setitem to be spelled as follows:
> 
>  def setitem(container, setitemf, name, object):
>      setContained(container, setitemf, container.get, name, object)
> 
> 3. Rename 'container' arg in containedEvent and uncontained to 'parent'
> 
> I agree with the decision to implement the container bookkeeping 
> directly in container classes rather than through adapters -- it's much 
> simpler and therefore less prone to error. However, I think the current 
> implementation is a bit too inflexible. These proposed changes are an 
> attempt to add enough flexibility without having to resort to adaptation.
> 
>  -- Garrett





More information about the Zope3-dev mailing list