##############################################################################
#
# Copyright (c) 2003 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Container-related interfaces
"""
__docformat__ = 'restructuredtext'
import zope.deferredimport
from zope.interface import Interface
from zope.interface import Invalid
from zope.interface.common.mapping import IEnumerableMapping
from zope.interface.common.mapping import IItemMapping
from zope.interface.common.mapping import IReadMapping
from zope.lifecycleevent.interfaces import IObjectModifiedEvent
from zope.schema import Set
from zope.container.i18n import ZopeMessageFactory as _
zope.deferredimport.initialize()
zope.deferredimport.deprecated(
"Some zope.container interfaces have been moved to"
" zope.lifecycleevent.interfaces, please import form there.",
IObjectAddedEvent='zope.lifecycleevent.interfaces:IObjectAddedEvent',
IObjectMovedEvent='zope.lifecycleevent.interfaces:IObjectMovedEvent',
IObjectRemovedEvent='zope.lifecycleevent.interfaces:IObjectRemovedEvent',
)
zope.deferredimport.deprecated(
"Some zope.container interfaces have been moved to"
" zope.location.interfaces, please import form there.",
IContained='zope.location.interfaces:IContained',
ILocation='zope.location.interfaces:ILocation',
)
[docs]
class DuplicateIDError(KeyError):
pass
[docs]
class ContainerError(Exception):
"""An error of a container with one of its components."""
[docs]
class InvalidContainerType(Invalid, TypeError):
"""The type of a container is not valid."""
[docs]
class InvalidItemType(Invalid, TypeError):
"""The type of an item is not valid."""
[docs]
class InvalidType(Invalid, TypeError):
"""The type of an object is not valid."""
[docs]
class IItemContainer(IItemMapping):
"""Minimal readable container."""
[docs]
class ISimpleReadContainer(IItemContainer, IReadMapping):
"""Readable content containers."""
[docs]
class IReadContainer(ISimpleReadContainer, IEnumerableMapping):
"""Readable containers that can be enumerated."""
[docs]
class IWriteContainer(Interface):
"""An interface for the write aspects of a container."""
def __setitem__(name, object):
"""Add the given `object` to the container under the given name.
Raises a ``TypeError`` if the key is not a unicode or ascii string.
Raises a ``ValueError`` if the key is empty, or if the key contains
a character which is not allowed in an object name.
Raises a ``KeyError`` if the key violates a uniqueness constraint.
The container might choose to add a different object than the
one passed to this method.
If the object doesn't implement `IContained`, then one of two
things must be done:
1. If the object implements `ILocation`, then the `IContained`
interface must be declared for the object.
2. Otherwise, a `ContainedProxy` is created for the object and
stored.
The object's `__parent__` and `__name__` attributes are set to the
container and the given name.
If the old parent was ``None``, then an `IObjectAddedEvent` is
generated, otherwise, an `IObjectMovedEvent` is generated. An
`IContainerModifiedEvent` is generated for the container.
If the object replaces another object, then the old object is
deleted before the new object is added, unless the container
vetos the replacement by raising an exception.
If the object's `__parent__` and `__name__` were already set to
the container and the name, then no events are generated and
no hooks. This allows advanced clients to take over event
generation.
"""
def __delitem__(name):
"""Delete the named object from the container.
Raises a ``KeyError`` if the object is not found.
If the deleted object's `__parent__` and `__name__` match the
container and given name, then an `IObjectRemovedEvent` is
generated and the attributes are set to ``None``. If the object
can be adapted to `IObjectMovedEvent`, then the adapter's
`moveNotify` method is called with the event.
Unless the object's `__parent__` and `__name__` attributes were
initially ``None``, generate an `IContainerModifiedEvent` for the
container.
If the object's `__parent__` and `__name__` were already set to
``None``, then no events are generated. This allows advanced
clients to take over event generation.
"""
[docs]
class IItemWriteContainer(IWriteContainer, IItemContainer):
"""A write container that also supports minimal reads."""
[docs]
class IContainer(IReadContainer, IWriteContainer):
"""Readable and writable content container."""
[docs]
class IContentContainer(IContainer):
"""A container that is to be used as a content type."""
[docs]
class IBTreeContainer(IContainer):
"""Container that supports BTree semantics for some methods."""
def items(key=None):
"""Return an iterator over the key-value pairs in the container.
If ``None`` is passed as `key`, this method behaves as if no argument
were passed; exactly as required for ``IContainer.items()``.
If `key` is in the container, the first item provided by the iterator
will correspond to that key. Otherwise, the first item will be for
the key that would come next if `key` were in the container.
"""
def keys(key=None):
"""Return an iterator over the keys in the container.
If ``None`` is passed as `key`, this method behaves as if no argument
were passed; exactly as required for ``IContainer.keys()``.
If `key` is in the container, the first key provided by the iterator
will be that key. Otherwise, the first key will be the one that would
come next if `key` were in the container.
"""
def values(key=None):
"""Return an iterator over the values in the container.
If ``None`` is passed as `key`, this method behaves as if no argument
were passed; exactly as required for ``IContainer.values()``.
If `key` is in the container, the first value provided by the iterator
will correspond to that key. Otherwise, the first value will be for
the key that would come next if `key` were in the container.
"""
[docs]
class IOrdered(Interface):
"""Objects whose contents are maintained in order."""
def updateOrder(order):
"""Revise the order of keys, replacing the current ordering.
order is a list or a tuple containing the set of existing keys in
the new order. `order` must contain ``len(keys())`` items and cannot
contain duplicate keys.
Raises ``TypeError`` if order is not a tuple or a list.
Raises ``ValueError`` if order contains an invalid set of keys.
"""
[docs]
class IOrderedContainer(IOrdered, IContainer):
"""Containers whose contents are maintained in order."""
[docs]
class IContainerNamesContainer(IContainer):
"""Containers that always choose names for their items."""
[docs]
class IReservedNames(Interface):
"""A sequence of names that are reserved for that container"""
reservedNames = Set(
title=_('Reserved Names'),
description=_('Names that are not allowed for addable content'),
required=True,
)
[docs]
class NameReserved(ValueError):
__doc__ = _("""The name is reserved for this container""")
##############################################################################
# Adding objects
[docs]
class UnaddableError(ContainerError):
"""An object cannot be added to a container."""
def __init__(self, container, obj, message=""):
self.container = container
self.obj = obj
self.message = message and ": %s" % message
def __str__(self):
return ("%(obj)s cannot be added "
"to %(container)s%(message)s" % self.__dict__)
class INameChooser(Interface):
def checkName(name, object):
"""Check whether an object name is valid.
Raises a user error if the name is not valid.
"""
def chooseName(name, object):
"""Choose a unique valid name for the object.
The given name and object may be taken into account when
choosing the name.
chooseName is expected to always choose a valid name (that would pass
the checkName test) and never raise an error.
"""
##############################################################################
# Modifying containers
[docs]
class IContainerModifiedEvent(IObjectModifiedEvent):
"""The container has been modified.
This event is specific to "containerness" modifications, which means
addition, removal or reordering of sub-objects.
"""
##############################################################################
# Finding objects
[docs]
class IFind(Interface):
"""
Find support for containers.
"""
def find(id_filters=None, object_filters=None):
"""Find object that matches all filters in all sub-objects.
This container itself is not included.
"""
class IObjectFindFilter(Interface):
def matches(object):
"""Return True if the object matches the filter criteria."""
class IIdFindFilter(Interface):
def matches(id):
"""Return True if the id matches the filter criteria."""