# HG changeset patch # User Thierry Florac # Date 1547025294 -3600 # Node ID 6e45323eb9074a7bf279fe45202adf7f58fabf1d # Parent 8745764c251fcfcb73cbb24414bd9bc410739dd0 Updated namespace traverser to add support for URLs like "/+/oid" diff -r 8745764c251f -r 6e45323eb907 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):