1
=========================
2
Containment constraints
3
=========================
5
Containment constraints allow us to express restrictions on the types
6
of items that can be placed in containers or on the types of
7
containers an item can be placed in. We express these constraints in
8
interfaces. Let's define some container and item interfaces:
10
>>> from zope.container.interfaces import IContainer
11
>>> from zope.location.interfaces import IContained
12
>>> from zope.container.constraints import containers, contains
14
>>> class IBuddyFolder(IContainer):
15
... contains('.IBuddy')
18
In this example, we used the contains function to declare that objects
19
that provide IBuddyFolder can only contain items that provide IBuddy.
20
Note that we used a string containing a dotted name for the IBuddy
21
interface. This is because IBuddy hasn't been defined yet. When we
22
define IBuddy, we can use IBuddyFolder directly:
24
>>> class IBuddy(IContained):
25
... containers(IBuddyFolder)
28
Now, with these interfaces in place, we can define Buddy and
29
BuddyFolder classes and verify that we can put buddies in buddy
32
>>> from zope import interface
34
>>> @interface.implementer(IBuddy)
38
>>> @interface.implementer(IBuddyFolder)
39
... class BuddyFolder:
42
>>> from zope.container.constraints import checkObject, checkFactory
43
>>> from zope.component.factory import Factory
45
>>> checkObject(BuddyFolder(), 'x', Buddy())
46
>>> checkFactory(BuddyFolder(), 'x', Factory(Buddy))
49
If we try to use other containers or folders, we'll get errors:
51
>>> @interface.implementer(IContainer)
55
>>> @interface.implementer(IContained)
59
>>> checkObject(Container(), 'x', Buddy())
60
... # doctest: +ELLIPSIS
61
Traceback (most recent call last):
62
InvalidContainerType: ...
64
>>> checkFactory(Container(), 'x', Factory(Buddy))
67
>>> checkObject(BuddyFolder(), 'x', Contained())
68
... # doctest: +ELLIPSIS
69
Traceback (most recent call last):
72
>>> checkFactory(BuddyFolder(), 'x', Factory(Contained))
75
In the example, we defined the container first and then the items. We
76
could have defined these in the opposite order:
78
>>> class IContact(IContained):
79
... containers('.IContacts')
81
>>> class IContacts(IContainer):
82
... contains(IContact)
84
>>> @interface.implementer(IContact)
88
>>> @interface.implementer(IContacts)
92
>>> checkObject(Contacts(), 'x', Contact())
94
>>> checkFactory(Contacts(), 'x', Factory(Contact))
97
>>> checkObject(Contacts(), 'x', Buddy())
98
... # doctest: +ELLIPSIS
99
Traceback (most recent call last):
102
>>> checkFactory(Contacts(), 'x', Factory(Buddy))
105
The constraints prevent us from moving a container beneath itself (either into
106
itself or another folder beneath it):
108
>>> container = Container()
109
>>> checkObject(container, 'x', container)
110
Traceback (most recent call last):
111
TypeError: Cannot add an object to itself or its children.
113
>>> import zope.location.interfaces
114
>>> import zope.interface
115
>>> subcontainer = Container()
116
>>> zope.interface.directlyProvides(subcontainer,
117
... zope.location.interfaces.ILocation)
118
>>> subcontainer.__parent__ = container
119
>>> checkObject(subcontainer, 'x', container)
120
Traceback (most recent call last):
121
TypeError: Cannot add an object to itself or its children.