Updated namespace traverser to add support for URLs like "/+/oid"
authorThierry Florac <tflorac@ulthar.net>
Wed, 09 Jan 2019 10:14:54 +0100
changeset 308 6e45323eb907
parent 307 8745764c251f
child 309 927860b990e3
Updated namespace traverser to add support for URLs like "/+/oid"
src/pyams_utils/traversing.py
--- a/src/pyams_utils/traversing.py	Mon Jan 07 11:34:52 2019 +0100
+++ b/src/pyams_utils/traversing.py	Wed Jan 09 10:14:54 2019 +0100
@@ -15,22 +15,22 @@
 
 # import standard library
 
-# import interfaces
-from pyams_utils.interfaces.traversing import IPathElements
+from pyramid.compat import decode_path_info, is_nonstr_iter
+from pyramid.exceptions import NotFound, URLDecodeError
 from pyramid.interfaces import VH_ROOT_KEY
+from pyramid.location import lineage
+from pyramid.threadlocal import get_current_registry
+from pyramid.traversal import ResourceTreeTraverser, empty, slash, split_path_info
+from zope.interface import Interface
 from zope.intid.interfaces import IIntIds
 from zope.location.interfaces import IContained
-from zope.traversing.interfaces import ITraversable, BeforeTraverseEvent
+from zope.traversing.interfaces import BeforeTraverseEvent, ITraversable
 
 # import packages
-from pyams_utils.adapter import adapter_config, ContextAdapter
+from pyams_utils.adapter import ContextAdapter, adapter_config
+# import interfaces
+from pyams_utils.interfaces.traversing import IPathElements
 from pyams_utils.registry import query_utility
-from pyramid.compat import decode_path_info, is_nonstr_iter
-from pyramid.exceptions import URLDecodeError, NotFound
-from pyramid.location import lineage
-from pyramid.threadlocal import get_current_registry
-from pyramid.traversal import ResourceTreeTraverser, slash, split_path_info, empty
-from zope.interface import Interface
 
 
 class NamespaceTraverser(ResourceTreeTraverser):
@@ -42,7 +42,8 @@
     - support for namespaces with "++" notation
     """
 
-    NAMESPACE_SELECTOR = '++'
+    PLUS_SELECTOR = '+'
+    NAMESPACE_SELECTOR = PLUS_SELECTOR * 2
 
     def __call__(self, request):
 
@@ -102,64 +103,88 @@
             # pushing and popping temporary lists for speed purposes
             # and this hurts readability; apologies
             i = 0
+            plus_selector = self.PLUS_SELECTOR
+            ns_selector = self.NAMESPACE_SELECTOR
             view_selector = self.VIEW_SELECTOR
-            ns_selector = self.NAMESPACE_SELECTOR
             vpath_tuple = split_path_info(vpath)
 
             for segment in vpath_tuple:
                 if ob is not root:
                     request.registry.notify(BeforeTraverseEvent(ob, request))
 
-                if segment[:2] == view_selector:
-                    # check for view name prefixed by '@@'
-                    return {'context': ob,
-                            'view_name': segment[2:],
-                            'subpath': vpath_tuple[i + 1:],
-                            'traversed': vpath_tuple[:vroot_idx + i + 1],
-                            'virtual_root': vroot,
-                            'virtual_root_path': vroot_tuple,
-                            'root': root}
+                if segment == plus_selector:
+                    # check for custom namespace called '+'
+                    # currently this namespace is used in PyAMS_default_theme package to get direct access to a given
+                    # content
+                    registry = get_current_registry()
+                    traverser = registry.queryMultiAdapter((ob, request), ITraversable, '+')
+                    if traverser is None:
+                        raise NotFound()
+                    ob = traverser.traverse(vpath_tuple[vroot_idx + i + 2], vpath_tuple[vroot_idx + i + 3:])
+                    i += 1
+                    return {
+                        'context': ob,
+                        'view_name': ''.join(vpath_tuple[vroot_idx + i + 2:]),
+                        'subpath': vpath_tuple[i + 2:],
+                        'traversed': vpath_tuple[:vroot_idx + i + 2],
+                        'virtual_root': vroot,
+                        'virtual_root_path': vroot_tuple,
+                        'root': root
+                    }
 
                 elif segment[:2] == ns_selector:
                     # check for namespace prefixed by '++'
                     # when a namespace is detected, named "ITraversable" multi-adapters are searched for
-                    # context and request, for context and for request, sequentially; a NotFound exception is
-                    # raised if traverser can't be found, otherwise it's "traverse" method is called to get new
-                    # context
+                    # context and request, or for context, sequentially; a NotFound exception is raised if traverser
+                    # can't be found, otherwise it's "traverse" method is called to get new context
                     ns, name = segment[2:].split(ns_selector, 1)
                     registry = get_current_registry()
                     traverser = registry.queryMultiAdapter((ob, request), ITraversable, ns)
                     if traverser is None:
                         traverser = registry.queryAdapter(ob, ITraversable, ns)
                     if traverser is None:
-                        traverser = registry.queryAdapter(request, ITraversable, ns)
-                    if traverser is None:
                         raise NotFound()
                     ob = traverser.traverse(name, vpath_tuple[vroot_idx + i + 1:])
                     i += 1
                     continue
 
+                elif segment[:2] == view_selector:
+                    # check for view name prefixed by '@@'
+                    return {
+                        'context': ob,
+                        'view_name': segment[2:],
+                        'subpath': vpath_tuple[i + 1:],
+                        'traversed': vpath_tuple[:vroot_idx + i + 1],
+                        'virtual_root': vroot,
+                        'virtual_root_path': vroot_tuple,
+                        'root': root
+                    }
+
                 try:
                     getitem = ob.__getitem__
                 except AttributeError:
-                    return {'context': ob,
-                            'view_name': segment,
-                            'subpath': vpath_tuple[i + 1:],
-                            'traversed': vpath_tuple[:vroot_idx + i + 1],
-                            'virtual_root': vroot,
-                            'virtual_root_path': vroot_tuple,
-                            'root': root}
+                    return {
+                        'context': ob,
+                        'view_name': segment,
+                        'subpath': vpath_tuple[i + 1:],
+                        'traversed': vpath_tuple[:vroot_idx + i + 1],
+                        'virtual_root': vroot,
+                        'virtual_root_path': vroot_tuple,
+                        'root': root
+                    }
 
                 try:
                     next = getitem(segment)
                 except KeyError:
-                    return {'context': ob,
-                            'view_name': segment,
-                            'subpath': vpath_tuple[i + 1:],
-                            'traversed': vpath_tuple[:vroot_idx + i + 1],
-                            'virtual_root': vroot,
-                            'virtual_root_path': vroot_tuple,
-                            'root': root}
+                    return {
+                        'context': ob,
+                        'view_name': segment,
+                        'subpath': vpath_tuple[i + 1:],
+                        'traversed': vpath_tuple[:vroot_idx + i + 1],
+                        'virtual_root': vroot,
+                        'virtual_root_path': vroot_tuple,
+                        'root': root
+                    }
                 if i == vroot_idx:
                     vroot = next
                 ob = next
@@ -168,13 +193,15 @@
         if ob is not root:
             request.registry.notify(BeforeTraverseEvent(ob, request))
 
-        return {'context': ob,
-                'view_name': empty,
-                'subpath': subpath,
-                'traversed': vpath_tuple,
-                'virtual_root': vroot,
-                'virtual_root_path': vroot_tuple,
-                'root': root}
+        return {
+            'context': ob,
+            'view_name': empty,
+            'subpath': subpath,
+            'traversed': vpath_tuple,
+            'virtual_root': vroot,
+            'virtual_root_path': vroot_tuple,
+            'root': root
+        }
 
 
 def get_parent(context, interface=Interface, allow_context=True, condition=None):