##############################################################################
#
# Copyright (c) 2002 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.
#
##############################################################################
"""Interfaces to do with traversing.
"""
from zope.interface import Attribute
from zope.interface import Interface
from zope.interface import implementer
from zope.interface.interfaces import IObjectEvent
from zope.interface.interfaces import ObjectEvent
# BBB: Re-import symbols to their old location.
from zope.location.interfaces import LocationError as TraversalError
from zope.location.interfaces import IRoot as IContainmentRoot
from zope.location.interfaces import ILocationInfo as IPhysicallyLocatable
_RAISE_KEYERROR = object()
[docs]class ITraversable(Interface):
"""To traverse an object, this interface must be provided"""
def traverse(name, furtherPath):
"""Get the next item on the path
Should return the item corresponding to 'name' or raise
:exc:`~zope.location.interfaces.LocationError` where appropriate.
:param str name: an ASCII string or Unicode object.
:param list furtherPath: is a list of names still to be
traversed. This method is allowed to change the contents
of furtherPath.
"""
[docs]class ITraverser(Interface):
"""Provide traverse features"""
# XXX This is used like a utility but implemented as an adapter: The
# traversal policy is only implemented once and repeated for all objects
# along the path.
def traverse(path, default=_RAISE_KEYERROR):
"""Return an object given a path.
Path is either an immutable sequence of strings or a slash ('/')
delimited string.
If the first string in the path sequence is an empty string, or the
path begins with a '/', start at the root. Otherwise the path is
relative to the current context.
If the object is not found, return *default* argument.
"""
[docs]class ITraversalAPI(Interface):
"""
Common API functions to ease traversal computations.
This is provided by :mod:`zope.traversing.api`.
"""
def joinPath(path, *args):
"""Join the given relative paths to the given *path*.
Returns a text (`unicode`) path.
The path should be well-formed, and not end in a '/' unless it is
the root path. It can be either a string (ascii only) or unicode.
The positional arguments are relative paths to be added to the
path as new path segments. The path may be absolute or relative.
A segment may not start with a '/' because that would be confused
with an absolute path. A segment may not end with a '/' because we
do not allow '/' at the end of relative paths. A segment may
consist of '.' or '..' to mean "the same place", or "the parent path"
respectively. A '.' should be removed and a '..' should cause the
segment to the left to be removed. ``joinPath('/', '..')`` should
raise an exception.
"""
def getPath(obj):
"""Returns a string representing the physical path to the object.
"""
def getRoot(obj):
"""Returns the root of the traversal for the given object.
"""
def traverse(object, path, default=None, request=None):
"""Traverse *path* relative to the given object.
:param str path: a string with path segments separated by '/'.
:keyword request: Passed in when traversing from
presentation code. This allows paths like "@@foo" to work.
:raises zope.location.interfaces.LocationError: if *path* cannot be found
.. note:: Calling `traverse` with a path argument taken from an
untrusted source, such as an HTTP request form variable,
is a bad idea. It could allow a maliciously constructed
request to call code unexpectedly. Consider using
`traverseName` instead.
"""
def traverseName(obj, name, default=None, traversable=None,
request=None):
"""Traverse a single step *name* relative to the given object.
*name* must be a string. '.' and '..' are treated specially,
as well as names starting with '@' or '+'. Otherwise *name*
will be treated as a single path segment.
You can explicitly pass in an `ITraversable` as the
*traversable* argument. If you do not, the given object will
be adapted to `ITraversable`.
*request* is passed in when traversing from presentation code.
This allows paths like "@@foo" to work.
:raises zope.location.interfaces.LocationError: if *path* cannot
be found and *default* was not provided.
"""
def getName(obj):
"""Get the name an object was traversed via
"""
def getParent(obj):
"""Returns the container the object was traversed via.
Returns `None` if the object is a containment root.
:raises TypeError: if the object doesn't have enough context
to get the parent.
"""
def getParents(obj):
"""
Returns a list starting with the given object's parent
followed by each of its parents.
:raises TypeError: if the context doesn't go all the way down
to a containment root.
"""
def canonicalPath(path_or_object):
"""
Returns a canonical absolute unicode path for the path or object.
Resolves segments that are '.' or '..'.
:raises ValueError: if a badly formed path is given.
"""
[docs]class IPathAdapter(Interface):
"""Marker interface for adapters to be used in paths
"""
[docs]class IEtcNamespace(Interface):
"""Marker for utility registrations in the ++etc++ namespace
"""
[docs]class IBeforeTraverseEvent(IObjectEvent):
"""An event which gets sent on publication traverse"""
request = Attribute("The current request")
@implementer(IBeforeTraverseEvent)
[docs]class BeforeTraverseEvent(ObjectEvent):
"""
An event which gets sent on publication traverse.
Default implementation of `IBeforeTraverseEvent`.
"""
def __init__(self, ob, request):
ObjectEvent.__init__(self, ob)
self.request = request