[Zope3-checkins] CVS: Zope3/src/zope/app/container - add.py:1.1.2.1 contained.py:1.1.2.1 remove.py:1.1.2.1 configure.zcml:1.18.2.1 copypastemove.py:1.10.24.1 find.py:1.7.2.1 ordered.py:1.5.10.1 sample.py:1.7.24.1 zopecontainer.py:NONE

Jim Fulton jim at zope.com
Mon Sep 8 15:22:21 EDT 2003


Update of /cvs-repository/Zope3/src/zope/app/container
In directory cvs.zope.org:/tmp/cvs-serv20092/src/zope/app/container

Modified Files:
      Tag: parentgeddon-branch
	configure.zcml copypastemove.py find.py ordered.py sample.py 
Added Files:
      Tag: parentgeddon-branch
	add.py contained.py remove.py 
Removed Files:
      Tag: parentgeddon-branch
	zopecontainer.py 
Log Message:
Checking in work in progress on parentgeddon-branch so Fred can help
me to get the tests passing.  Specific log entries will be provided
when we merge this into the head.


=== Added File Zope3/src/zope/app/container/add.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Object adding support

This module provides an IAddTarget implementation.  Add targets are
helper adapters that help satisfy framework-defined constraints when
additing objects:

- If an object is add notifiable, call the after add hook.

- Generate an ObjectAdded event for the object added.

- Generate an ObjectModified event for the container.

Notes:

  - We really should get rid of the hooks and handle them via
    subscriptions.

  - Other frameworks might impose different constraints, which is
    why this functionality of provided via adaptation rather than
    fixed apis.

$Id: add.py,v 1.1.2.1 2003/09/08 18:21:19 jim Exp $
"""

import zope.interface
from zope.exceptions import DuplicationError
from zope.app.interfaces import container
from zope.app import zapi
from zope.app.event import objectevent, publish
from zope.app.interfaces.container import IAddNotifiable
from zope.proxy import removeAllProxies

class AddTarget:
    """Adapter that helps when adding objects.

    """

    zope.interface.implements(container.IAddTarget)

    def __init__(self, context):
        self.context = context

    def acceptsObject(self, name, obj):
        """Check that an object can be added to a container

        >>> class Sample(dict):
        ...    def setObject(self, name, value):
        ...        self[name] = value
        ...        return name
        
        >>> sample = Sample()

        >>> target = AddTarget(sample)

        We require unicode names or string names that can be converted
        to unicode.

        >>> target.acceptsObject(u'bob', 1)
        1
        >>> target.acceptsObject('bob', 1)
        1
        >>> target.acceptsObject('bob\200', 1)
        0
        >>> target.acceptsObject(1, 1)
        0

        We don't allow empty names, or names starting with '@' or '+':

        >>> target.acceptsObject('', 1)
        0
        >>> target.acceptsObject('@@bob', 1)
        0
        >>> target.acceptsObject('+bob', 1)
        0

        Or names containing /s:

        >>> target.acceptsObject('bill/ted', 1)
        0

        We also require names to be new:

        >>> x = sample.setObject('bob', 1)
        >>> target.acceptsObject(u'bob', 1)
        0

        """

        if not name:
            return False

        if isinstance(name, str):
            try: name = unicode(name)
            except UnicodeError:
                return False
        elif not isinstance(name, unicode):
            return False
        
        if name[:1] in '+@' or '/' in name:
            return False

        if name in self.context:
            return False

        return True

    def addObject(self, name, obj):
        """Add an object

        Let's look at an example:

        >>> class SampleContainer(dict):
        ...    def setObject(self, name, value):
        ...        self[name] = value
        ...        return name

        >>> container = SampleContainer()

        >>> class SampleItem:
        ...     zope.interface.implements(IAddNotifiable)
        ...     def afterAddHook(self, object, container):
        ...         self.hookargs = object, container

        >>> import zope.app.tests.placelesssetup
        >>> from zope.app.event.tests.placelesssetup import getEvents
        >>> zope.app.tests.placelesssetup.setUp()

        >>> target = AddTarget(container)
        >>> item = SampleItem()

        We'll get an error if we give an invalid name:
        
        >>> target.addObject(1, item)
        Traceback (most recent call last):
        ...
        ValueError: Invalid name

        and won't get any events:

        >>> getEvents()
        []

        But, if we use a valid name:
        
        >>> target.addObject('bob', item)
        'bob'

        We'll get an add event and a modification event generated:

        >>> len(getEvents())
        2
        >>> from zope.app.interfaces.event import IObjectAddedEvent 
        >>> getEvents(IObjectAddedEvent)[0].object is item
        1
        >>> from zope.app.interfaces.event import IObjectModifiedEvent 
        >>> getEvents(IObjectModifiedEvent)[0].object is container
        1
        
        The hook method on the item was also called:

        >>> item.hookargs == (item, container)
        1
        
        >>> zope.app.tests.placelesssetup.tearDown()
        """

        if not self.acceptsObject(name, obj):
            raise ValueError("Invalid name")
            
        container = self.context

        # We remove the proxies from the object before adding it to
        # the container, because we can't store proxies.
        obj = removeAllProxies(obj)

        # Add the object
        name = container.setObject(name, obj)

        # Publish an added event
        # We explicitly get the object back from the container with
        # container[name], because some kinds of container may choose
        # to store a different object than the exact one we added.
        publish(container, objectevent.ObjectAddedEvent(obj))

        # Call the after add hook, if necessary
        adapter = zapi.queryAdapter(obj, IAddNotifiable)
        if adapter is not None:
            adapter.afterAddHook(obj, container)

        publish(container, objectevent.ObjectModifiedEvent(container))
        return name


=== Added File Zope3/src/zope/app/container/contained.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Classes to support implenting IContained

$Id: contained.py,v 1.1.2.1 2003/09/08 18:21:19 jim Exp $
"""

import zope.interface
from zope.app.interfaces.container import IContained
from zope.app.interfaces.location import ILocation
from zope.proxy import ProxyBase, getProxiedObject
from zope.app.decorator import DecoratorSpecificationDescriptor
from zope.app.decorator import DecoratedSecurityCheckerDescriptor

class Contained(object):
    """Stupid mix-in that defines __parent__ and __name__ attributes
    """

    zope.interface.implements(IContained)

    __parent__ = __name__ = None

def contained(object, container, name=None):
    """Establish the containment of the object in the container

    If the object implements IContained, simply set it's __parent__
    and __name attributes:

    >>> container = {}
    >>> item = Contained()
    >>> x = contained(item, container, 'foo')
    >>> x is item
    1
    >>> item.__parent__ is container
    1
    >>> item.__name__
    'foo'
    
    If the object implements ILocation, but not IContained, set it's
    __parent__ and __name__ attributes *and* declare that it
    implements IContained:

    >>> from zope.app.location import Location
    >>> item = Location()
    >>> IContained.isImplementedBy(item)
    0
    >>> x = contained(item, container, 'foo')
    >>> x is item
    1
    >>> item.__parent__ is container
    1
    >>> item.__name__
    'foo'
    >>> IContained.isImplementedBy(item)
    1

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

    >>> item = []
    >>> x = contained(item, container, 'foo')
    >>> x is item
    0
    >>> x.__parent__ is container
    1
    >>> x.__name__
    'foo'
    
    """

    if not IContained.isImplementedBy(object):
        if ILocation.isImplementedBy(object):
            zope.interface.directlyProvides(object, IContained)
        else:
            object = ContainedProxy(object)

    object.__parent__ = container
    object.__name__ = name

    return object

def uncontained(object):
    """Clear the containment relationship between the object amd the container

    >>> container = {}
    >>> item = Contained()
    >>> x = contained(item, container, 'foo')
    >>> x is item
    1
    >>> item.__parent__ is container
    1
    >>> item.__name__
    'foo'

    >>> x = uncontained(item)
    >>> item.__parent__ is container
    False
    >>> item.__name__    
    
    """

    object.__parent__ = object.__name__ = None

class ContainedProxy(ProxyBase):
    """Contained-object proxy

    This is a picklable proxy that can be put around objects that
    don't implemeny IContained.

    >>> l = [1, 2, 3]
    >>> p = ContainedProxy(l, "Dad", "p")
    >>> p
    [1, 2, 3]
    >>> p.__parent__
    'Dad'
    >>> p.__name__
    'p'

    >>> import pickle
    >>> p2 = pickle.loads(pickle.dumps(p))
    >>> p2
    [1, 2, 3]
    >>> p2.__parent__
    'Dad'
    >>> p2.__name__
    'p'
    
    
    """

    zope.interface.implements(IContained)

    __slots__ = '__parent__', '__name__'
    __safe_for_unpickling__ = True

    def __new__(self, ob, container=None, name=None):
        return ProxyBase.__new__(self, ob)

    def __init__(self, ob, container=None, name=None):
        ProxyBase.__init__(self, ob)
        self.__parent__ = container
        self.__name__ = name

    def __reduce__(self, proto=None):
        return (ContainedProxy,
                (getProxiedObject(self), 
                 self.__parent__, self.__name__),
                )

    __reduce_ex__ = __reduce__

    __providedBy__ = DecoratorSpecificationDescriptor()

    __Security_checker__ = DecoratedSecurityCheckerDescriptor()

    def _p_oid(self):
        # I'm not persistent, Dagnabit.
        raise AttributeError, '_p_oid'

    _p_oid = property(_p_oid)


=== Added File Zope3/src/zope/app/container/remove.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Object removing support

This module provides an IAddSource implementation.  Remove sources are
helper adapters that help satisfy framework-defined constraints when
removing objects:

- If an object is delete notifiable, call the before delete hook.

- Generate an ObjectRemoved event for the object removed.

- Generate an ObjectModified event for the container.

Notes:

  - We really should get rid of the hooks and handle them via
    subscriptions.

  - Other frameworks might impose different constraints, which is
    why this functionality of provided via adaptation rather than
    fixed apis.

$Id: remove.py,v 1.1.2.1 2003/09/08 18:21:19 jim Exp $
"""
import zope.interface
from zope.app.interfaces import container
from zope.app import zapi
from zope.app.event import objectevent, publish
from zope.app.interfaces.container import IDeleteNotifiable

class RemoveSource:
    """Adapter that helps when removing objects.

    """

    zope.interface.implements(container.IRemoveSource)
    
    def __init__(self, context):
        self.context = context

    def removeObject(self, key):
        """Remove an object

        Let's look at an example:

        >>> class SampleItem:
        ...     zope.interface.implements(IDeleteNotifiable)
        ...     def beforeDeleteHook(self, object, container):
        ...         self.hookargs = object, container


        >>> import zope.app.tests.placelesssetup
        >>> from zope.app.event.tests.placelesssetup import getEvents
        >>> zope.app.tests.placelesssetup.setUp()

        >>> item = SampleItem()
        >>> container = {'bob': item}
        >>> source = RemoveSource(container)

        We'll get an error if we give an invalid name:
        
        >>> source.removeObject('bob') is item
        1
        >>> container
        {}

        We'll get an removed event and a modification event generated:

        >>> len(getEvents())
        2
        >>> from zope.app.interfaces.event import IObjectRemovedEvent 
        >>> getEvents(IObjectRemovedEvent)[0].object is item
        1
        >>> from zope.app.interfaces.event import IObjectModifiedEvent 
        >>> getEvents(IObjectModifiedEvent)[0].object is container
        1
        
        The hook method on the item was also called:

        >>> item.hookargs == (item, container)
        1
        
        >>> zope.app.tests.placelesssetup.tearDown()
        """
        container = self.context

        object = container[key]

        # Call the before delete hook, if necessary
        adapter = zapi.queryAdapter(object, IDeleteNotifiable)
        if adapter is not None:
            adapter.beforeDeleteHook(object, container)

        publish(container, objectevent.ObjectRemovedEvent(object))

        del container[key]

        publish(container, objectevent.ObjectModifiedEvent(container))
        
        return object


=== Zope3/src/zope/app/container/configure.zcml 1.18 => 1.18.2.1 ===
--- Zope3/src/zope/app/container/configure.zcml:1.18	Fri Sep  5 14:43:20 2003
+++ Zope3/src/zope/app/container/configure.zcml	Mon Sep  8 14:21:19 2003
@@ -31,50 +31,23 @@
       for="zope.app.interfaces.container.IReadContainer"
       />
 
-  <!-- Decorators for different flavours of container -->
-  <adapter
-      factory="zope.app.container.zopecontainer.ZopeItemContainerDecorator"
-      provides="zope.app.interfaces.context.IZopeContextWrapper"
-      for="zope.app.interfaces.container.IItemContainer"
-      />
-  <adapter
-      factory="
-      zope.app.container.zopecontainer.ZopeSimpleReadContainerDecorator"
-      provides="zope.app.interfaces.context.IZopeContextWrapper"
-      for="zope.app.interfaces.container.ISimpleReadContainer"
-      />
+
   <adapter
-      factory="
-      zope.app.container.zopecontainer.ZopeReadContainerDecorator"
-      provides="zope.app.interfaces.context.IZopeContextWrapper"
+      factory="zope.app.container.size.ContainerSized"
+      provides="zope.app.interfaces.size.ISized"
       for="zope.app.interfaces.container.IReadContainer"
       />
+
   <adapter
-      factory="
-      zope.app.container.zopecontainer.ZopeItemWriteContainerDecorator"
-      provides="zope.app.interfaces.context.IZopeContextWrapper"
-      for="zope.app.interfaces.container.IItemWriteContainer"
-      />
-  <adapter
-      factory="zope.app.container.zopecontainer.ZopeContainerDecorator"
-      provides="zope.app.interfaces.context.IZopeContextWrapper"
+      provides="zope.app.interfaces.container.IAddTarget"
       for="zope.app.interfaces.container.IContainer"
+      factory=".add.AddTarget"
       />
 
-  <!-- XXX There is a sticky question of what permission 'rename' should have.
-           See src/zope/app/context.txt for further discussion.
-    -->
-  <class class="zope.app.container.zopecontainer.ZopeContainerDecorator">
-    <require
-        attributes="rename"
-        permission="zope.ManageServices"
-        />
-  </class>
-
   <adapter
-      factory="zope.app.container.size.ContainerSized"
-      provides="zope.app.interfaces.size.ISized"
-      for="zope.app.interfaces.container.IReadContainer"
+      provides="zope.app.interfaces.container.IRemoveSource"
+      for="zope.app.interfaces.container.IContainer"
+      factory=".remove.RemoveSource"
       />
 
   <event:subscribe


=== Zope3/src/zope/app/container/copypastemove.py 1.10 => 1.10.24.1 ===
--- Zope3/src/zope/app/container/copypastemove.py:1.10	Sun Jun  8 12:39:46 2003
+++ Zope3/src/zope/app/container/copypastemove.py	Mon Sep  8 14:21:19 2003
@@ -24,13 +24,12 @@
 from zope.app.interfaces.container import IContainerNamesContainer
 from zope.app.interfaces.container import ICopySource, INoChildrenCopySource
 from zope.app.interfaces.container import IMoveSource
-from zope.app.interfaces.container import IOptionalNamesContainer
 from zope.app.interfaces.container import IPasteNamesChooser
 from zope.app.interfaces.container import IPasteTarget
 from zope.app.interfaces.content.folder import ICloneWithoutChildren
+from zope.app.location import locationCopy
 from zope.component import getAdapter
 from zope.interface import implements
-from zope.app.context import ContextWrapper
 from zope.proxy import removeAllProxies
 import copy
 
@@ -43,8 +42,7 @@
         self.context = container
 
     def acceptsObject(self, key, obj):
-        '''Allow the container to say if it accepts the given wrapped
-        object.
+        '''Allow the container to say if it accepts the given object.
 
         Returns True if the object would be accepted as contents of
         this container. Otherwise, returns False.
@@ -77,8 +75,7 @@
         container = self.context
 
         if not key:
-            if not (IOptionalNamesContainer.isImplementedBy(container)
-                    or IContainerNamesContainer.isImplementedBy(container)):
+            if not IContainerNamesContainer.isImplementedBy(container):
                 raise ValueError("Empty names are not allowed")
 
         # We remove the proxies from the object before adding it to
@@ -117,7 +114,6 @@
         container = self.context
 
         object = container[key]
-        object = ContextWrapper(object, container, name=key)
 
         # here, we dont call the before delete hook
         del container[key]
@@ -140,11 +136,11 @@
 
         copyingTo is the unicode path for where the copy is to.
         '''
+        
         value = self.context.get(key, None)
         if value is not None:
             value = removeAllProxies(value)
-            value = copy.deepcopy(value)
-            return ContextWrapper(value, self.context, name=key)
+            return locationCopy(value)
 
 
 class NoChildrenCopySource:
@@ -165,8 +161,7 @@
             value = removeAllProxies(value)
             if not ICloneWithoutChildren.isImplementedBy(value):
                 return None
-            value = value.cloneWithoutChildren()
-            return ContextWrapper(value, self.context, name=key)
+            return value.cloneWithoutChildren()
 
 class PasteNamesChooser:
 


=== Zope3/src/zope/app/container/find.py 1.7 => 1.7.2.1 ===
--- Zope3/src/zope/app/container/find.py:1.7	Fri Sep  5 14:43:20 2003
+++ Zope3/src/zope/app/container/find.py	Mon Sep  8 14:21:19 2003
@@ -19,8 +19,6 @@
 from zope.app.interfaces.find import IFind, IIdFindFilter
 from zope.app.interfaces.container import IReadContainer
 from zope.interface import implements
-# XXX need to do this manually to wrap objects
-from zope.app.context import ContextWrapper
 
 class FindAdapter(object):
 
@@ -38,7 +36,6 @@
         result = []
         container = self._context
         for id, object in container.items():
-            object = ContextWrapper(object, container, name=id)
             _find_helper(id, object, container,
                          id_filters, object_filters,
                          result)
@@ -64,7 +61,6 @@
 
     container = object
     for id, object in container.items():
-        object = ContextWrapper(object, container, name=id)
         _find_helper(id, object, container, id_filters, object_filters, result)
 
 class SimpleIdFindFilter(object):


=== Zope3/src/zope/app/container/ordered.py 1.5 => 1.5.10.1 ===
--- Zope3/src/zope/app/container/ordered.py:1.5	Fri Aug  1 15:56:49 2003
+++ Zope3/src/zope/app/container/ordered.py	Mon Sep  8 14:21:19 2003
@@ -23,8 +23,9 @@
 from persistence.dict import PersistentDict
 from persistence.list import PersistentList
 from types import StringTypes, TupleType, ListType
+from zope.app.container.contained import Contained, contained, uncontained
 
-class OrderedContainer(Persistent):
+class OrderedContainer(Persistent, Contained):
     """ OrderedContainer maintains entries' order as added and moved.
 
     >>> oc = OrderedContainer()
@@ -194,7 +195,8 @@
                             "ascii or unicode string" % key)
         if len(key) == 0:
             raise ValueError("The key cannot be an empty string")
-        self._data[key] = object
+
+        self._data[key] = contained(object, self, key)
 
         if not existed:
             self._order.append(key)
@@ -221,6 +223,7 @@
         1
         """
 
+        uncontained(self._data[key])
         del self._data[key]
         self._order.remove(key)
 


=== Zope3/src/zope/app/container/sample.py 1.7 => 1.7.24.1 ===
--- Zope3/src/zope/app/container/sample.py:1.7	Tue Jun  3 10:19:31 2003
+++ Zope3/src/zope/app/container/sample.py	Mon Sep  8 14:21:19 2003
@@ -26,8 +26,9 @@
 
 from zope.app.interfaces.container import IContainer
 from zope.interface import implements
+from zope.app.container.contained import Contained, contained, uncontained
 
-class SampleContainer(object):
+class SampleContainer(Contained):
     """Sample container implementation suitable for testing.
 
     It is not suitable, directly as a base class unless the subclass
@@ -97,9 +98,11 @@
                             "ascii or unicode string" % key)
         if len(key) == 0:
             raise ValueError("The key cannot be an empty string")
-        self.__data[key] = object
+
+        self.__data[key] = contained(object, self, key)
         return key
 
     def __delitem__(self, key):
         '''See interface IWriteContainer'''
+        uncontained(self.__data[key])
         del self.__data[key]

=== Removed File Zope3/src/zope/app/container/zopecontainer.py ===




More information about the Zope3-Checkins mailing list