[Checkins] SVN: zope.traversing/trunk/src/zope/traversing/ Moved the publicationtraverse module from zope.app.publication and added tests.

Shane Hathaway shane at hathawaymix.org
Fri May 22 20:35:47 EDT 2009


Log message for revision 100262:
  Moved the publicationtraverse module from zope.app.publication and added tests.
  

Changed:
  A   zope.traversing/trunk/src/zope/traversing/publicationtraverse.py
  A   zope.traversing/trunk/src/zope/traversing/tests/test_publicationtraverse.py

-=-
Added: zope.traversing/trunk/src/zope/traversing/publicationtraverse.py
===================================================================
--- zope.traversing/trunk/src/zope/traversing/publicationtraverse.py	                        (rev 0)
+++ zope.traversing/trunk/src/zope/traversing/publicationtraverse.py	2009-05-23 00:35:46 UTC (rev 100262)
@@ -0,0 +1,129 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation 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.
+#
+##############################################################################
+"""Publication Traverser
+
+$Id: publicationtraverse.py 67630 2006-04-27 00:54:03Z jim $
+"""
+__docformat__ = 'restructuredtext'
+from types import StringTypes
+
+from zope.component import queryMultiAdapter
+from zope.publisher.interfaces import NotFound
+from zope.security.checker import ProxyFactory
+from zope.traversing.namespace import namespaceLookup
+from zope.traversing.namespace import nsParse
+from zope.traversing.interfaces import TraversalError
+from zope.publisher.interfaces import IPublishTraverse
+from zope.publisher.interfaces.browser import IBrowserPublisher
+
+class DuplicateNamespaces(Exception):
+    """More than one namespace was specified in a request"""
+
+class UnknownNamespace(Exception):
+    """A parameter specified an unknown namespace"""
+
+class PublicationTraverser(object):
+    """Traversal used for publication.
+
+    The significant differences from
+    zope.traversing.adapters.traversePathElement() are:
+
+    - Instead of adapting each traversed object to ITraversable, this
+      version multi-adapts (ob, request) to IPublishTraverse.
+
+    - This version wraps a security proxy around each traversed object.
+
+    - This version raises NotFound rather than LocationError.
+
+    - This version has a method, traverseRelativeURL(), that
+      supports "browserDefault" traversal.
+    """
+
+    def traverseName(self, request, ob, name):
+        nm = name # the name to look up the object with
+
+        if name and name[:1] in '@+':
+            # Process URI segment parameters.
+            ns, nm = nsParse(name)
+            if ns:
+                try:
+                    ob2 = namespaceLookup(ns, nm, ob, request)
+                except TraversalError:
+                    raise NotFound(ob, name)
+
+                return ProxyFactory(ob2)
+
+        if nm == '.':
+            return ob
+
+        if IPublishTraverse.providedBy(ob):
+            ob2 = ob.publishTraverse(request, nm)
+        else:
+            # self is marker
+            adapter = queryMultiAdapter((ob, request), IPublishTraverse,
+                                        default=self)
+            if adapter is not self:
+                ob2 = adapter.publishTraverse(request, nm)
+            else:
+                raise NotFound(ob, name, request)
+
+        return ProxyFactory(ob2)
+
+    def traversePath(self, request, ob, path):
+
+        if isinstance(path, StringTypes):
+            path = path.split('/')
+            if len(path) > 1 and not path[-1]:
+                # Remove trailing slash
+                path.pop()
+        else:
+            path = list(path)
+
+        # Remove single dots
+        path = [x for x in path if x != '.']
+
+        path.reverse()
+
+        # Remove double dots
+        while '..' in path:
+            l = path.index('..')
+            if l < 0 or l+2 > len(path):
+                break
+            del path[l:l+2]
+
+        pop = path.pop
+
+        while path:
+            name = pop()
+            ob = self.traverseName(request, ob, name)
+
+        return ob
+
+    def traverseRelativeURL(self, request, ob, path):
+        """Path traversal that includes browserDefault paths"""
+        ob = self.traversePath(request, ob, path)
+
+        while True:
+            adapter = IBrowserPublisher(ob, None)
+            if adapter is None:
+                return ob
+            ob, path = adapter.browserDefault(request)
+            ob = ProxyFactory(ob)
+            if not path:
+                return ob
+
+            ob = self.traversePath(request, ob, path)
+
+# alternate spelling
+PublicationTraverse = PublicationTraverser

Added: zope.traversing/trunk/src/zope/traversing/tests/test_publicationtraverse.py
===================================================================
--- zope.traversing/trunk/src/zope/traversing/tests/test_publicationtraverse.py	                        (rev 0)
+++ zope.traversing/trunk/src/zope/traversing/tests/test_publicationtraverse.py	2009-05-23 00:35:46 UTC (rev 100262)
@@ -0,0 +1,183 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation 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.
+#
+##############################################################################
+"""Tests of PublicationTraverser
+
+$Id: test_vh.py 82003 2007-11-28 08:49:12Z jukart $
+"""
+from unittest import TestCase, main, makeSuite
+from zope.testing.cleanup import CleanUp
+from zope.component import provideAdapter
+from zope.interface import Interface, implements
+from zope.publisher.browser import TestRequest
+from zope.publisher.interfaces import IPublishTraverse
+from zope.publisher.interfaces import NotFound
+from zope.publisher.interfaces.browser import IBrowserPublisher
+from zope.security.proxy import removeSecurityProxy
+from zope.traversing.interfaces import ITraversable
+
+class TestPublicationTraverser(CleanUp, TestCase):
+
+    def testViewNotFound(self):
+        ob = Content()
+        from zope.traversing.publicationtraverse import PublicationTraverser
+        t = PublicationTraverser()
+        request = TestRequest()
+        self.assertRaises(NotFound, t.traverseName, request, ob, '@@foo')
+
+    def testViewFound(self):
+        provideAdapter(DummyViewTraverser, (Interface, Interface),
+            ITraversable, name='view')
+        ob = Content()
+        from zope.traversing.publicationtraverse import PublicationTraverser
+        t = PublicationTraverser()
+        request = TestRequest()
+        proxy = t.traverseName(request, ob, '@@foo')
+        view = removeSecurityProxy(proxy)
+        self.assertTrue(proxy is not view)
+        self.assertEqual(view.__class__, View)
+        self.assertEqual(view.name, 'foo')
+
+    def testDot(self):
+        ob = Content()
+        from zope.traversing.publicationtraverse import PublicationTraverser
+        t = PublicationTraverser()
+        request = TestRequest()
+        self.assertEqual(ob, t.traverseName(request, ob, '.'))
+
+    def testNameNotFound(self):
+        ob = Content()
+        from zope.traversing.publicationtraverse import PublicationTraverser
+        t = PublicationTraverser()
+        request = TestRequest()
+        self.assertRaises(NotFound, t.traverseName, request, ob, 'foo')
+
+    def testNameFound(self):
+        provideAdapter(DummyPublishTraverse, (Interface, Interface),
+            IPublishTraverse)
+        ob = Content()
+        from zope.traversing.publicationtraverse import PublicationTraverser
+        t = PublicationTraverser()
+        request = TestRequest()
+        proxy = t.traverseName(request, ob, 'foo')
+        view = removeSecurityProxy(proxy)
+        self.assertTrue(proxy is not view)
+        self.assertEqual(view.__class__, View)
+        self.assertEqual(view.name, 'foo')
+
+    def testDirectTraversal(self):
+        request = TestRequest()
+        ob = DummyPublishTraverse(Content(), request)
+        from zope.traversing.publicationtraverse import PublicationTraverser
+        t = PublicationTraverser()
+        proxy = t.traverseName(request, ob, 'foo')
+        view = removeSecurityProxy(proxy)
+        self.assertTrue(proxy is not view)
+        self.assertEqual(view.__class__, View)
+        self.assertEqual(view.name, 'foo')
+
+    def testPathNotFound(self):
+        ob = Content()
+        from zope.traversing.publicationtraverse import PublicationTraverser
+        t = PublicationTraverser()
+        request = TestRequest()
+        self.assertRaises(NotFound, t.traversePath, request, ob, 'foo/bar')
+
+    def testPathFound(self):
+        provideAdapter(DummyPublishTraverse, (Interface, Interface),
+            IPublishTraverse)
+        ob = Content()
+        from zope.traversing.publicationtraverse import PublicationTraverser
+        t = PublicationTraverser()
+        request = TestRequest()
+        proxy = t.traversePath(request, ob, 'foo/bar')
+        view = removeSecurityProxy(proxy)
+        self.assertTrue(proxy is not view)
+        self.assertEqual(view.__class__, View)
+        self.assertEqual(view.name, 'bar')
+
+    def testComplexPath(self):
+        provideAdapter(DummyPublishTraverse, (Interface, Interface),
+            IPublishTraverse)
+        ob = Content()
+        from zope.traversing.publicationtraverse import PublicationTraverser
+        t = PublicationTraverser()
+        request = TestRequest()
+        proxy = t.traversePath(request, ob, 'foo/../alpha//beta/./bar')
+        view = removeSecurityProxy(proxy)
+        self.assertTrue(proxy is not view)
+        self.assertEqual(view.__class__, View)
+        self.assertEqual(view.name, 'bar')
+
+    def testTraverseRelativeURL(self):
+        provideAdapter(DummyPublishTraverse, (Interface, Interface),
+            IPublishTraverse)
+        provideAdapter(DummyBrowserPublisher, (Interface,),
+            IBrowserPublisher)
+        ob = Content()
+        from zope.traversing.publicationtraverse import PublicationTraverser
+        t = PublicationTraverser()
+        request = TestRequest()
+        proxy = t.traverseRelativeURL(request, ob, 'foo/bar')
+        view = removeSecurityProxy(proxy)
+        self.assertTrue(proxy is not view)
+        self.assertEqual(view.__class__, View)
+        self.assertEqual(view.name, 'more')
+
+
+class IContent(Interface):
+    pass
+
+class Content(object):
+    implements(IContent)
+
+class View(object):
+    def __init__(self, name):
+        self.name = name
+
+class DummyViewTraverser(object):
+    implements(ITraversable)
+
+    def __init__(self, content, request):
+        self.content = content
+
+    def traverse(self, name, furtherPath):
+        return View(name)
+
+class DummyPublishTraverse(object):
+    implements(IPublishTraverse)
+
+    def __init__(self, context, request):
+        pass
+
+    def publishTraverse(self, request, name):
+        return View(name)
+
+class DummyBrowserPublisher(object):
+    implements(IBrowserPublisher)
+
+    def __init__(self, context):
+        self.context = removeSecurityProxy(context)
+
+    def browserDefault(self, request):
+        if self.context.name != 'more':
+            return self.context, ['more']
+        else:
+            return self.context, ()
+
+
+def test_suite():
+    return makeSuite(TestPublicationTraverser)
+
+if __name__ == '__main__':
+    unittest.main()



More information about the Checkins mailing list