[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/container/ Allow dotted names to be used in containment constraints. This solves

Jim Fulton jim at zope.com
Thu Sep 29 05:12:50 EDT 2005


Log message for revision 38683:
  Allow dotted names to be used in containment constraints.  This solves
  the chicken-and-egg problem.
  

Changed:
  U   Zope3/trunk/src/zope/app/container/constraints.py
  A   Zope3/trunk/src/zope/app/container/constraints.txt
  U   Zope3/trunk/src/zope/app/container/tests/test_constraints.py

-=-
Modified: Zope3/trunk/src/zope/app/container/constraints.py
===================================================================
--- Zope3/trunk/src/zope/app/container/constraints.py	2005-09-29 09:12:45 UTC (rev 38682)
+++ Zope3/trunk/src/zope/app/container/constraints.py	2005-09-29 09:12:49 UTC (rev 38683)
@@ -153,6 +153,8 @@
 
 import sys
 
+from zope.cachedescriptors.property import readproperty
+from zope.dottedname.resolve import resolve
 import zope.schema
 
 from zope.interface import providedBy
@@ -219,7 +221,6 @@
 
     return True
 
-
 class IItemTypePrecondition(zope.interface.Interface):
 
     def __call__(container, name, object):
@@ -234,7 +235,29 @@
         Return a boolean value.
         """
 
-class ItemTypePrecondition(object):
+class _TypesBased(object):
+
+    @readproperty
+    def types(self):
+        raw_types, module = self.raw_types
+        types = []
+        for t in raw_types:
+            if isinstance(t, str):
+                t = resolve(t, module)
+            types.append(t)
+
+        self.types = types
+        return types
+
+    def __init__(self, *types, **kw):
+        if [t for t in types if isinstance(t, str)]:
+            # have dotted names
+            module = kw.get('module', sys._getframe(1).f_globals['__name__'])
+            self.raw_types = types, module
+        else:
+            self.types = types
+
+class ItemTypePrecondition(_TypesBased):
     """Specify a `__setitem__` precondition that restricts item types
 
     Items must be one of the given types.  
@@ -283,9 +306,6 @@
 
     zope.interface.implements(IItemTypePrecondition)
 
-    def __init__(self, *types):
-        self.types = types
-
     def __call__(self, container, name, object):
         for iface in self.types:
             if iface.providedBy(object):
@@ -340,7 +360,10 @@
     def __setitem__(key, value):
         pass
     __setitem__.__doc__ = IContainer['__setitem__'].__doc__
-    __setitem__.precondition = ItemTypePrecondition(*types)
+    __setitem__.precondition = ItemTypePrecondition(
+        *types,
+        **dict(module=f_globals['__name__'])
+        )
     f_locals['__setitem__'] = __setitem__
 
 
@@ -354,7 +377,7 @@
         """
 
 
-class ContainerTypesConstraint(object):
+class ContainerTypesConstraint(_TypesBased):
     """Constrain a container to be one of a number of types
 
     >>> class I1(zope.interface.Interface):
@@ -381,9 +404,6 @@
 
     zope.interface.implements(IContainerTypesConstraint)
 
-    def __init__(self, *types):
-        self.types = types
-
     def __call__(self, object):
        for iface in self.types:
            if iface.providedBy(object):
@@ -431,7 +451,10 @@
         raise TypeError("containers not called from suite")
 
     __parent__ = zope.schema.Field(
-        constraint = ContainerTypesConstraint(*types)
+        constraint = ContainerTypesConstraint(
+            *types,
+            **dict(module=f_globals['__name__'])
+            )
         )
     f_locals['__parent__'] = __parent__
 

Added: Zope3/trunk/src/zope/app/container/constraints.txt
===================================================================
--- Zope3/trunk/src/zope/app/container/constraints.txt	2005-09-29 09:12:45 UTC (rev 38682)
+++ Zope3/trunk/src/zope/app/container/constraints.txt	2005-09-29 09:12:49 UTC (rev 38683)
@@ -0,0 +1,97 @@
+Containment constraints
+=======================
+
+Containment constraints allow us to express restrictions on the types
+of items that can be placed in containers or on the types of
+containers an item can be placed in.  We express these constraints in
+interfaces.  Let's define some container and item interfaces:
+
+    >>> from zope.app.container.interfaces import IContainer, IContained
+    >>> from zope.app.container.constraints import containers, contains    
+
+    >>> class IBuddyFolder(IContainer):
+    ...     contains('.IBuddy')
+
+
+In this example, we used the contains function to declare that objects
+that provide IBuddyFolder can only contain items that provide IBuddy.
+Note that we used a string containing a dotted name for the IBuddy
+interface. This is because IBuddy hasn't been defined yet.  When we
+define IBuddy, we can use IBuddyFolder directly:
+
+    >>> class IBuddy(IContained):
+    ...     containers(IBuddyFolder)
+
+ 
+Now, with these interfaces in place, we can define Buddy and
+BuddyFolder classes and verify that we can put buddies in buddy
+folders:
+
+    >>> from zope import interface
+
+    >>> class Buddy:
+    ...     interface.implements(IBuddy)
+
+    >>> class BuddyFolder:
+    ...     interface.implements(IBuddyFolder)
+
+    >>> from zope.app.container.constraints import checkObject, checkFactory
+    >>> from zope.component.factory import Factory
+
+    >>> checkObject(BuddyFolder(), 'x', Buddy())
+    >>> checkFactory(BuddyFolder(), 'x', Factory(Buddy))
+    True
+
+If we try to use other containers or folders, we'll get errors:
+
+    >>> class Container:
+    ...     interface.implements(IContainer)
+
+    >>> class Contained:
+    ...     interface.implements(IContained)
+
+    >>> checkObject(Container(), 'x', Buddy())
+    ... # doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    InvalidContainerType: ...
+
+    >>> checkFactory(Container(), 'x', Factory(Buddy))
+    False
+
+    >>> checkObject(BuddyFolder(), 'x', Contained())
+    ... # doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    InvalidItemType: ...
+
+    >>> checkFactory(BuddyFolder(), 'x', Factory(Contained))
+    False
+
+In the example, we defined the container first and then the items.  We
+could have defined these in the opposite order:
+
+    >>> class IContact(IContained):
+    ...     containers('.IContacts')
+
+    >>> class IContacts(IContainer):
+    ...     contains(IContact)
+
+    >>> class Contact:
+    ...     interface.implements(IContact)
+
+    >>> class Contacts:
+    ...     interface.implements(IContacts)
+
+    >>> checkObject(Contacts(), 'x', Contact())
+
+    >>> checkFactory(Contacts(), 'x', Factory(Contact))
+    True
+
+    >>> checkObject(Contacts(), 'x', Buddy())
+    ... # doctest: +ELLIPSIS
+    Traceback (most recent call last):
+    InvalidItemType: ...
+
+    >>> checkFactory(Contacts(), 'x', Factory(Buddy))
+    False
+    
+


Property changes on: Zope3/trunk/src/zope/app/container/constraints.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/trunk/src/zope/app/container/tests/test_constraints.py
===================================================================
--- Zope3/trunk/src/zope/app/container/tests/test_constraints.py	2005-09-29 09:12:45 UTC (rev 38682)
+++ Zope3/trunk/src/zope/app/container/tests/test_constraints.py	2005-09-29 09:12:49 UTC (rev 38683)
@@ -16,11 +16,16 @@
 $Id$
 """
 import unittest
-from zope.testing.doctestunit import DocTestSuite
+from zope.testing import doctest, module
 
+def setUp(test):
+    module.setUp(test, 'zope.app.container.constraints_txt')
+
 def test_suite():
     return unittest.TestSuite((
-        DocTestSuite('zope.app.container.constraints'),
+        doctest.DocTestSuite('zope.app.container.constraints'),
+        doctest.DocFileSuite('../constraints.txt',
+                             setUp=setUp, tearDown=module.tearDown),
         ))
 
 if __name__ == '__main__': unittest.main()



More information about the Zope3-Checkins mailing list