[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