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

Jim Fulton jim at zope.com
Sat Jun 20 13:10:40 EDT 2009


Why?  traverseName is part of zope.app.publication's implementation.   
Now it's oddly split off in a very separate package. This makes  
customizing publication behavior more difficult. I recently made  
proxying overridable and missed traverseName.

This should be moved back to zope.app.publication. The only other  
thing that uses this is zope.app.publsher.browser.menu. That can and  
should get to these methods via request.publication.

Or, better yet, traverseRelativeURL and traversePath should be moved  
to the browser module and should get traverseName from  
request.publication.

I'll go ahead and do this.

Jim


On May 22, 2009, at 8:35 PM, Shane Hathaway wrote:

> 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()
>
> _______________________________________________
> Checkins mailing list
> Checkins at zope.org
> http://mail.zope.org/mailman/listinfo/checkins

--
Jim Fulton
Zope Corporation




More information about the Zope-Dev mailing list