[Zope-Checkins] CVS: Zope3/lib/python/Zope/App/Traversing - DefaultTraversable.py:1.1.2.1 ITraversable.py:1.1.2.1 ITraverser.py:1.1.2.2 Traverser.py:1.1.2.2

Martijn Pieters mj@zope.com
Mon, 3 Dec 2001 17:08:13 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/App/Traversing
In directory cvs.zope.org:/tmp/cvs-serv23382/lib/python/Zope/App/Traversing

Modified Files:
      Tag: Zope-3x-branch
	ITraverser.py Traverser.py 
Added Files:
      Tag: Zope-3x-branch
	DefaultTraversable.py ITraversable.py 
Log Message:
Implementation of unrestrictedTraverse, with new interface and feature
ITraversable. Include a default ITraversable implementation, and one for
containers. Note: No security assertions yet.


=== Added File Zope3/lib/python/Zope/App/Traversing/DefaultTraversable.py ===
# Copyright (c) 2001 Zope Corporation and Contributors.  All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 1.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.

from ITraversable import ITraversable
from Zope.Exceptions import NotFoundError

class DefaultTraversable:
    """Traverses objects via attribute and dictionary lookup"""

    __implements__ = ITraversable

    def __init__(self, subject):
        self._subject = subject

    def traverse(self, name, furtherPath):
        if name.startswith('_'):
            raise NotFoundError, name

        if hasattr(self._subject, name):
            return getattr(self._subject, name)
        else:
            try:
                return self._subject[name]
            except (KeyError, IndexError, TypeError, AttributeError):
                raise NotFoundError, name



=== Added File Zope3/lib/python/Zope/App/Traversing/ITraversable.py ===
# Copyright (c) 2001 Zope Corporation and Contributors.  All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 1.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.

import Interface

class ITraversable(Interface.Base):
    """To traverse an object, this interface must be provided"""

    def traverse(name, furtherPath):
        """Get the next item on the path

        furtherPath is a list of names still to be traversed. This method is
        allowed to change the contents of furtherPath.

        Should return the item corresponding to 'name' or raise
        Zope.Exceptions.NotFoundError where appropriate.
        
        """


=== Zope3/lib/python/Zope/App/Traversing/ITraverser.py 1.1.2.1 => 1.1.2.2 ===
     def getPhysicalRoot():
         """
-        Returns the top-level Application object, or the bas eobject is it is
+        Returns the top-level Application object, or the base object if it is
         unwrapped.
         """
 
@@ -31,10 +31,14 @@
     
     def unrestrictedTraverse(path, default=_RAISE_KEYERROR):
         """
-        Return an object given a path (an immutable sequence of strings).
+        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, start at
-        the root. Otherwise the path is relative to the current context.
+        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 then the 'default' argument will be returned.
         """
@@ -44,6 +48,8 @@
         Return the object obtained by traversing the given path from the object
         on which the method was called, performing security checks along the
         way.
+
+        The path argument is the same as for unrestrictedTraverse.
                 
         If an object is not found then the 'default' argument will be returned.
         """


=== Zope3/lib/python/Zope/App/Traversing/Traverser.py 1.1.2.1 => 1.1.2.2 ===
 
 from ITraverser import ITraverser
+from ITraversable import ITraversable
 from Zope.ContextWrapper.IWrapper import IWrapper
 from Zope.ContextWrapper import wrapper
+from Zope.ComponentArchitecture import getFeature
+from Zope.Exceptions import NotFoundError
+
+from types import StringType
 
 from __future__ import generators
 
@@ -48,11 +53,58 @@
         return tuple(path)
     
     def unrestrictedTraverse(self, path, default=_marker, restricted=0):
-        # stub, never finds.
-        if default is not _marker:
-            return default
+        if not path: return self._wrapper
+
+        if type(path) is StringType:
+            path = path.split('/')
+            if len(path) > 1 and not path[-1]:
+                # Remove trailing slash
+                path.pop()
+        else:
+            path = list(path)
+
+        path.reverse()
+        pop = path.pop
+
+        curr = self._wrapper
+        if not path[-1]:
+            # Start at the root
+            pop()
+            curr = self.getPhysicalRoot()
+            if restricted: pass
+                # TODO: security restrictions
+
+        try:
+            while path:
+                name = pop()
+
+                if name == '.':
+                    continue
+
+                if name == '..':
+                    next = wrapper.getcontext(curr) or curr
+                    if next is not None:
+                        # TODO: security restrictions
+                        curr = next
+                        continue
+
+                traversable = getFeature(curr, ITraversable, None)
+                if traversable is None:
+                    # Cannot be traversed, so we cannot find the next name
+                    raise NotFoundError, name
+
+                else:
+                    next = traversable.traverse(name, path)
+                    next = wrapper.Wrapper(next, curr, name=name)
+                    if restricted: pass
+                        # TODO: security restrictions
+                    curr = next
 
-        raise KeyError
+            return curr
+
+        except:
+            if default == _marker: raise
+            return default
 
     def restrictedTraverse(self, path, default=_marker):
         self.unrestrictedTraverse(self, path, default, restricted=1)