summaryrefslogtreecommitdiff
path: root/src/zope/traversing/namespace.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/zope/traversing/namespace.py')
-rw-r--r--src/zope/traversing/namespace.py311
1 files changed, 218 insertions, 93 deletions
diff --git a/src/zope/traversing/namespace.py b/src/zope/traversing/namespace.py
index 7ef07a4..ef296e3 100644
--- a/src/zope/traversing/namespace.py
+++ b/src/zope/traversing/namespace.py
@@ -11,7 +11,48 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""URL Namespace Implementations
+"""
+URL Namespace Implementations
+
+A URL Namespace is usually a path segment that looks like ``++ns++name``.
+(It can also look like ``@@name``, which is a
+shortcut for ``++view++name``. See :func:`nsParse` for details.)
+
+``ns`` is the name of the namespace (a named, registered adapter that
+implements `ITraversable`) and ``name`` is the name to traverse to in
+that namespace.
+
+The function :func:`namespaceLookup` handles this process.
+
+If you configure this package by loading its ``configure.zcml`` using
+:mod:`zope.configuration.xmlconfig`, several namespaces are registered. They
+are registered both as single adapters for any object, and as
+multi-adapters (views) for any object together with a
+`zope.publisher.interfaces.IRequest`. Those namespaces are:
+
+etc
+ Implemented in `etc`
+attribute
+ Implemented in `attr`
+adapter
+ Implemented in `adapter`
+item
+ Implemented in `item`
+acquire
+ Implemented in `acquire`
+view
+ Implemented in `view`
+resource
+ Implemented in `resource`
+lang
+ Implemented in `lang`
+skin
+ Implemented in `skin`
+vh
+ Implemented in `vh`
+debug
+ Implemented in `debug` (only if the ZCML feature ``devmode`` is enabled)
+ and only registered as a multi-adapter.
"""
__docformat__ = 'restructuredtext'
@@ -41,68 +82,71 @@ class ExcessiveDepth(LocationError):
def namespaceLookup(ns, name, object, request=None):
- """Lookup a value from a namespace
+ """
+ Lookup a value from a namespace.
- We look up a value using a view or an adapter, depending on
- whether a request is passed.
+ We look up a value by getting an adapter from the *object* to
+ :class:`~zope.traversing.interfaces.ITraversable` named *ns*. If
+ the *request* is passed, we get a multi-adapter on the *object*
+ and *request* (sometimes this is called a "view").
- Let's start with adapter-based transersal:
+ Let's start with adapter-based traversal::
- >>> class I(zope.interface.Interface):
- ... 'Test interface'
- >>> @zope.interface.implementer(I)
- ... class C(object):
- ... pass
+ >>> class I(zope.interface.Interface):
+ ... 'Test interface'
+ >>> @zope.interface.implementer(I)
+ ... class C(object):
+ ... pass
- We'll register a simple testing adapter:
+ We'll register a simple testing adapter::
- >>> class Adapter(object):
- ... def __init__(self, context):
- ... self.context = context
- ... def traverse(self, name, remaining):
- ... return name+'42'
+ >>> class Adapter(object):
+ ... def __init__(self, context):
+ ... self.context = context
+ ... def traverse(self, name, remaining):
+ ... return name+'42'
- >>> zope.component.provideAdapter(Adapter, (I,), ITraversable, 'foo')
+ >>> zope.component.provideAdapter(Adapter, (I,), ITraversable, 'foo')
Then given an object, we can traverse it with a
- namespace-qualified name:
+ namespace-qualified name::
- >>> namespaceLookup('foo', 'bar', C())
- 'bar42'
+ >>> namespaceLookup('foo', 'bar', C())
+ 'bar42'
- If we give an invalid namespace, we'll get a not found error:
+ If we give an invalid namespace, we'll get a not found error::
- >>> namespaceLookup('fiz', 'bar', C()) # doctest: +ELLIPSIS
- Traceback (most recent call last):
- ...
- LocationError: (<zope.traversing.namespace.C object at 0x...>, '++fiz++bar')
+ >>> namespaceLookup('fiz', 'bar', C()) # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ LocationError: (<zope.traversing.namespace.C object at 0x...>, '++fiz++bar')
- We'll get the same thing if we provide a request:
+ We'll get the same thing if we provide a request::
- >>> from zope.publisher.browser import TestRequest
- >>> request = TestRequest()
- >>> namespaceLookup('foo', 'bar', C(), request) # doctest: +ELLIPSIS
- Traceback (most recent call last):
- ...
- LocationError: (<zope.traversing.namespace.C object at 0x...>, '++foo++bar')
+ >>> from zope.publisher.browser import TestRequest
+ >>> request = TestRequest()
+ >>> namespaceLookup('foo', 'bar', C(), request) # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ LocationError: (<zope.traversing.namespace.C object at 0x...>, '++foo++bar')
- We need to provide a view:
+ We need to provide a view::
- >>> class View(object):
- ... def __init__(self, context, request):
- ... pass
- ... def traverse(self, name, remaining):
- ... return name+'fromview'
- >>> from zope.traversing.testing import browserView
- >>> browserView(I, 'foo', View, providing=ITraversable)
+ >>> class View(object):
+ ... def __init__(self, context, request):
+ ... pass
+ ... def traverse(self, name, remaining):
+ ... return name+'fromview'
+ >>> from zope.traversing.testing import browserView
+ >>> browserView(I, 'foo', View, providing=ITraversable)
- >>> namespaceLookup('foo', 'bar', C(), request)
- 'barfromview'
+ >>> namespaceLookup('foo', 'bar', C(), request)
+ 'barfromview'
- Clean up:
+ Clean up::
- >>> from zope.testing.cleanup import cleanUp
- >>> cleanUp()
+ >>> from zope.testing.cleanup import cleanUp
+ >>> cleanUp()
"""
if request is not None:
traverser = zope.component.queryMultiAdapter((object, request),
@@ -120,32 +164,33 @@ namespace_pattern = re.compile('[+][+]([a-zA-Z0-9_]+)[+][+]')
def nsParse(name):
- """Parse a namespace-qualified name into a namespace name and a
- name. Returns the namespace name and a name.
-
- A namespace-qualified name is usually of the form ++ns++name, as in:
+ """
+ Parse a namespace-qualified name into a namespace name and a name.
+ Returns the namespace name and a name.
- >>> nsParse('++acquire++foo')
- ('acquire', 'foo')
+ A namespace-qualified name is usually of the form ++ns++name, as
+ in::
- The part inside the +s must be an identifier, so:
+ >>> nsParse('++acquire++foo')
+ ('acquire', 'foo')
- >>> nsParse('++hello world++foo')
- ('', '++hello world++foo')
- >>> nsParse('+++acquire+++foo')
- ('', '+++acquire+++foo')
+ The part inside the +s must be an identifier, so::
- But it may also be a @@foo, which implies the view namespace:
+ >>> nsParse('++hello world++foo')
+ ('', '++hello world++foo')
+ >>> nsParse('+++acquire+++foo')
+ ('', '+++acquire+++foo')
- >>> nsParse('@@foo')
- ('view', 'foo')
+ But it may also be a @@foo, which implies the view namespace::
- >>> nsParse('@@@foo')
- ('view', '@foo')
+ >>> nsParse('@@foo')
+ ('view', 'foo')
- >>> nsParse('@foo')
- ('', '@foo')
+ >>> nsParse('@@@foo')
+ ('view', '@foo')
+ >>> nsParse('@foo')
+ ('', '@foo')
"""
ns = ''
if name.startswith('@@'):
@@ -160,14 +205,14 @@ def nsParse(name):
return ns, name
-def getResource(site, name, request):
- resource = queryResource(site, name, request)
+def getResource(context, name, request):
+ resource = queryResource(context, name, request)
if resource is None:
- raise LocationError(site, name)
+ raise LocationError(context, name)
return resource
-def queryResource(site, name, request, default=None):
+def queryResource(context, name, request, default=None):
resource = zope.component.queryAdapter(request, name=name)
if resource is None:
return default
@@ -176,7 +221,7 @@ def queryResource(site, name, request, default=None):
# resource to do this. We still return the proxied resource.
r = removeSecurityProxy(resource)
- r.__parent__ = site
+ r.__parent__ = context
r.__name__ = name
return resource
@@ -188,28 +233,28 @@ def queryResource(site, name, request, default=None):
class SimpleHandler(object):
def __init__(self, context, request=None):
- """Simple handlers can be used as adapters or views
-
- They ignore their second constructor arg and store the first
- one in their context attr:
-
- >>> SimpleHandler(42).context
- 42
-
- >>> SimpleHandler(42, 43).context
- 42
+ """
+ It ignores its second constructor arg and stores the first
+ one in its ``context`` attr.
"""
self.context = context
class acquire(SimpleHandler):
- """Traversal adapter for the acquire namespace
+ """
+ Traversal adapter for the ``acquire`` namespace.
+
+ This namespace tries to traverse to the given *name*
+ starting with the context object. If it cannot be found,
+ it proceeds to look at each ``__parent__`` all the way
+ up the tree until it is found.
"""
def traverse(self, name, remaining):
- """Acquire a name
+ """
+ Acquire a name
- Let's set up some example data:
+ Let's set up some example data::
>>> @zope.interface.implementer(ITraversable)
... class testcontent(object):
@@ -272,6 +317,12 @@ class acquire(SimpleHandler):
class attr(SimpleHandler):
+ """
+ Traversal adapter for the ``attribute`` namespace.
+
+ This namespace simply looks for an attribute of the given
+ *name*.
+ """
def traverse(self, name, ignored):
"""Attribute traversal adapter
@@ -288,6 +339,12 @@ class attr(SimpleHandler):
class item(SimpleHandler):
+ """
+ Traversal adapter for the ``item`` namespace.
+
+ This namespace simply uses ``__getitem__`` to find a
+ value of the given *name*.
+ """
def traverse(self, name, ignored):
"""Item traversal adapter
@@ -303,6 +360,17 @@ class item(SimpleHandler):
class etc(SimpleHandler):
+ """
+ Traversal adapter for the ``etc`` namespace.
+
+ This namespace provides for a layer of indirection. The given
+ **name** is used to find a utility of that name that implements
+ `zope.traversing.interfaces.IEtcNamespace`.
+
+ As a special case, if there is no such utility, and the name is
+ "site", then we will attempt to call a method named ``getSiteManager``
+ on the *context* object.
+ """
def traverse(self, name, ignored):
utility = zope.component.queryUtility(IEtcNamespace, name)
@@ -327,6 +395,15 @@ class etc(SimpleHandler):
@zope.interface.implementer(ITraversable)
class view(object):
+ """
+ Traversal adapter for the ``view`` (``@@``) namespace.
+
+ This looks for the default multi-adapter from the *context* and
+ *request* of the given *name*.
+
+ :raises zope.location.interfaces.LocationError: If no such
+ adapter can be found.
+ """
def __init__(self, context, request):
self.context = context
@@ -342,6 +419,14 @@ class view(object):
class resource(view):
+ """
+ Traversal adapter for the ``resource`` namespace.
+
+ Resources are default adapters of the given *name* for the
+ *request* (**not** the *context*). The returned object will have
+ its ``__parent__`` set to the *context* and its ``__name__`` will
+ match the *name* we traversed.
+ """
def traverse(self, name, ignored):
# The context is important here, since it becomes the parent of the
@@ -350,6 +435,17 @@ class resource(view):
class lang(view):
+ """
+ Traversal adapter for the ``lang`` namespace.
+
+ Traversing to *name* means to adapt the request to
+ :class:`zope.i18n.interfaces.IModifiableUserPreferredLanguages`
+ and set the *name* as the only preferred language.
+
+ This needs the *request* to support
+ :class:`zope.publisher.interfaces.http.IVirtualHostRequest` because
+ it shifts the language name to the application.
+ """
def traverse(self, name, ignored):
self.request.shiftNameToApplication()
@@ -359,6 +455,18 @@ class lang(view):
class skin(view):
+ """
+ Traversal adapter for the ``skin`` namespace.
+
+ Traversing to *name* looks for the
+ :class:`zope.publisher.interfaces.browser.IBrowserSkinType`
+ utility having the given name, and then applies it to the
+ *request* with :func:`.applySkin`.
+
+ This needs the *request* to support
+ :class:`zope.publisher.interfaces.http.IVirtualHostRequest`
+ because it shifts the skin name to the application.
+ """
def traverse(self, name, ignored):
self.request.shiftNameToApplication()
@@ -371,6 +479,16 @@ class skin(view):
class vh(view):
+ """
+ Traversal adapter for the ``vh`` namespace.
+
+ Traversing to *name*, which must be of the form
+ ``protocol:host:port`` causes a call to
+ :meth:`zope.publisher.interfaces.http.IVirtualHostRequest.setApplicationServer`.
+ Segments in the request's traversal stack up to a prior ``++`` are
+ collected and become the application names given to
+ :meth:`zope.publisher.interfaces.http.IVirtualHostRequest.setVirtualHostRoot`.
+ """
def traverse(self, name, ignored):
@@ -410,13 +528,17 @@ class vh(view):
class adapter(SimpleHandler):
+ """
+ Traversal adapter for the ``adapter`` namespace.
- def traverse(self, name, ignored):
- """Adapter traversal adapter
+ This adapter provides traversal to named adapters for the
+ *context* registered to provide
+ `zope.traversing.interfaces.IPathAdapter`.
+ """""
- This adapter provides traversal to named adapters registered
- to provide IPathAdapter.
+ def traverse(self, name, ignored):
+ """
To demonstrate this, we need to register some adapters:
>>> def adapter1(ob):
@@ -454,15 +576,18 @@ class adapter(SimpleHandler):
class debug(view):
+ """
+ Traversal adapter for the ``debug`` namespace.
- enable_debug = __debug__
+ This adapter allows debugging flags to be set in the request.
- def traverse(self, name, ignored):
- """Debug traversal adapter
+ .. seealso:: :class:`zope.publisher.interfaces.IDebugFlags`
+ """
- This adapter allows debugging flags to be set in the request.
- See IDebugFlags.
+ enable_debug = __debug__
+ def traverse(self, name, ignored):
+ """
Setup for demonstration:
>>> from zope.publisher.browser import TestRequest
@@ -470,7 +595,7 @@ class debug(view):
>>> ob = object()
>>> adapter = debug(ob, request)
- in debug mode, ++debug++source enables source annotations
+ in debug mode, ``++debug++source`` enables source annotations
>>> request.debug.sourceAnnotations
False
@@ -479,7 +604,7 @@ class debug(view):
>>> request.debug.sourceAnnotations
True
- ++debug++tal enables TAL markup in output
+ ``++debug++tal`` enables TAL markup in output
>>> request.debug.showTAL
False
@@ -488,7 +613,7 @@ class debug(view):
>>> request.debug.showTAL
True
- ++debug++errors enables tracebacks (by switching to debug skin)
+ ``++debug++errors`` enables tracebacks (by switching to debug skin)
>>> from zope.publisher.interfaces.browser import IBrowserRequest