diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/zope/traversing/adapters.py | 46 | ||||
-rw-r--r-- | src/zope/traversing/api.py | 91 | ||||
-rw-r--r-- | src/zope/traversing/browser/absoluteurl.py | 16 | ||||
-rw-r--r-- | src/zope/traversing/browser/interfaces.py | 18 | ||||
-rw-r--r-- | src/zope/traversing/interfaces.py | 103 | ||||
-rw-r--r-- | src/zope/traversing/namespace.py | 311 | ||||
-rw-r--r-- | src/zope/traversing/publicationtraverse.py | 18 | ||||
-rw-r--r-- | src/zope/traversing/tests/test_namespacetrversal.py | 9 |
8 files changed, 384 insertions, 228 deletions
diff --git a/src/zope/traversing/adapters.py b/src/zope/traversing/adapters.py index 45d6e92..69ad836 100644 --- a/src/zope/traversing/adapters.py +++ b/src/zope/traversing/adapters.py @@ -11,7 +11,8 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -"""Adapters for the traversing mechanism +""" +Adapters for the traversing mechanism. """ import six @@ -30,7 +31,11 @@ _marker = object() # opaque marker that doesn't get security proxied @zope.interface.implementer(ITraversable) class DefaultTraversable(object): - """Traverses objects via attribute and item lookup""" + """ + Traverses objects via attribute and item lookup. + + Implements `~zope.traversing.interfaces.ITraversable`. + """ def __init__(self, subject): self._subject = subject @@ -58,7 +63,11 @@ class DefaultTraversable(object): @zope.interface.implementer(ITraverser) class Traverser(object): - """Provide traverse features""" + """ + Provide traverse features. + + Implements `~zope.traversing.interfaces.ITraverser`. + """ # This adapter can be used for any object. @@ -99,24 +108,29 @@ class Traverser(object): def traversePathElement(obj, name, further_path, default=_marker, traversable=None, request=None): - """Traverse a single step 'name' relative to the given object. - - 'name' must be a string. '.' and '..' are treated specially, as well as - names starting with '@' or '+'. Otherwise 'name' will be treated as a - single path segment. + """ + Traverse a single step *name* relative to the given object. - 'further_path' is a list of names still to be traversed. This method - is allowed to change the contents of 'further_path'. + This is used to implement + :meth:`zope.traversing.interfaces.ITraversalAPI.traverseName`. - You can explicitly pass in an ITraversable as the 'traversable' - argument. If you do not, the given object will be adapted to ITraversable. + :param str name: must be a string. '.' and '..' are treated + specially, as well as names starting with '@' or '+'. + Otherwise *name* will be treated as a single path segment. + :param list further_path: a list of names still to be traversed. + This method is allowed to change the contents of + *further_path*. - 'request' is passed in when traversing from presentation code. This - allows paths like @@foo to work. + :keyword ITraversable traversable: You can explicitly pass in + an `~zope.traversing.interfaces.ITraversable` as the + *traversable* argument. If you do not, the given object will + be adapted to ``ITraversable``. - Raises LocationError if path cannot be found and 'default' was - not provided. + :keyword request: assed in when traversing from presentation + code. This allows paths like ``@@foo`` to work. + :raises zope.location.interfaces.LocationError: if *path* cannot + be found and '*default* was not provided. """ __traceback_info__ = (obj, name) diff --git a/src/zope/traversing/api.py b/src/zope/traversing/api.py index 7a938b2..c9b0cb3 100644 --- a/src/zope/traversing/api.py +++ b/src/zope/traversing/api.py @@ -11,7 +11,10 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -"""Convenience functions for traversing the object tree. +""" +Convenience functions for traversing the object tree. + +This module provides :class:`zope.traversing.interfaces.ITraversalAPI` """ import six from zope.interface import moduleProvides @@ -26,22 +29,8 @@ _marker = object() def joinPath(path, *args): - """Join the given relative paths to the given path. - - Returns a unicode path. - - The path should be well-formed, and not end in a '/' unless it is - the root path. It can be either a string (ascii only) or unicode. - The positional arguments are relative paths to be added to the - path as new path segments. The path may be absolute or relative. - - A segment may not start with a '/' because that would be confused - with an absolute path. A segment may not end with a '/' because we - do not allow '/' at the end of relative paths. A segment may - consist of . or .. to mean "the same place", or "the parent path" - respectively. A '.' should be removed and a '..' should cause the - segment to the left to be removed. joinPath('/', '..') should - raise an exception. + """ + Join the given relative paths to the given path. """ if not args: @@ -58,32 +47,21 @@ def joinPath(path, *args): def getPath(obj): - """Returns a string representing the physical path to the object. + """ + Returns a string representing the physical path to the object. """ return ILocationInfo(obj).getPath() def getRoot(obj): - """Returns the root of the traversal for the given object. + """ + Returns the root of the traversal for the given object. """ return ILocationInfo(obj).getRoot() def traverse(object, path, default=_marker, request=None): - """Traverse 'path' relative to the given object. - - 'path' is a string with path segments separated by '/'. - - 'request' is passed in when traversing from presentation code. This - allows paths like @@foo to work. - - Raises LocationError if path cannot be found - - Note: calling traverse with a path argument taken from an untrusted - source, such as an HTTP request form variable, is a bad idea. - It could allow a maliciously constructed request to call - code unexpectedly. - Consider using traverseName instead. + """ """ traverser = ITraverser(object) if default is _marker: @@ -92,21 +70,8 @@ def traverse(object, path, default=_marker, request=None): def traverseName(obj, name, default=_marker, traversable=None, request=None): - """Traverse a single step 'name' relative to the given object. - - 'name' must be a string. '.' and '..' are treated specially, as well as - names starting with '@' or '+'. Otherwise 'name' will be treated as a - single path segment. - - You can explicitly pass in an ITraversable as the 'traversable' - argument. If you do not, the given object will be adapted to ITraversable. - - 'request' is passed in when traversing from presentation code. This - allows paths like @@foo to work. - - Raises LocationError if path cannot be found and 'default' was - not provided. - + """ + Traverse a single step 'name' relative to the given object. """ further_path = [] if default is _marker: @@ -122,17 +87,15 @@ def traverseName(obj, name, default=_marker, traversable=None, request=None): def getName(obj): - """Get the name an object was traversed via + """ + Get the name an object was traversed via """ return ILocationInfo(obj).getName() def getParent(obj): - """Returns the container the object was traversed via. - - Returns None if the object is a containment root. - Raises TypeError if the object doesn't have enough context to get the - parent. + """ + Returns the container the object was traversed via. """ try: location_info = ILocationInfo(obj) @@ -157,11 +120,9 @@ def getParent(obj): def getParents(obj): - """Returns a list starting with the given object's parent followed by + """ + Returns a list starting with the given object's parent followed by each of its parents. - - Raises a TypeError if the context doesn't go all the way down to - a containment root. """ return ILocationInfo(obj).getParents() @@ -194,11 +155,9 @@ def _normalizePath(path): def canonicalPath(path_or_object): - """Returns a canonical absolute unicode path for the given path or object. - - Resolves segments that are '.' or '..'. - - Raises ValueError if a badly formed path is given. + """ + Returns a canonical absolute unicode path for the given path or + object. """ if isinstance(path_or_object, six.string_types): path = path_or_object @@ -223,3 +182,9 @@ def canonicalPath(path_or_object): # import this down here to avoid circular imports from zope.traversing.adapters import traversePathElement + +# Synchronize the documentation. +for name in ITraversalAPI.names(): + if name in globals(): + globals()[name].__doc__ = ITraversalAPI[name].__doc__ +del name diff --git a/src/zope/traversing/browser/absoluteurl.py b/src/zope/traversing/browser/absoluteurl.py index 9b89d46..285b9de 100644 --- a/src/zope/traversing/browser/absoluteurl.py +++ b/src/zope/traversing/browser/absoluteurl.py @@ -11,7 +11,12 @@ # FOR A PARTICULAR PURPOSE # ############################################################################## -"""Absolute URL View components +""" +Absolute URL View components. + +These are registered as views and named views (``absolute_url``) if +you load this package's ``configure.zcml`` with +:mod:`zope.configuration.xmlconfig`. """ try: from urllib.parse import quote_from_bytes as quote @@ -52,6 +57,10 @@ class _EncodedUnicode(object): @implementer(IAbsoluteURL) class AbsoluteURL(_EncodedUnicode, BrowserView): + """ + The default implementation of + :class:`zope.traversing.browser.interfaces.IAbsoluteURL`. + """ def __str__(self): context = self.context @@ -131,6 +140,11 @@ class AbsoluteURL(_EncodedUnicode, @implementer(IAbsoluteURL) class SiteAbsoluteURL(_EncodedUnicode, BrowserView): + """ + An implementation of + :class:`zope.traversing.browser.interfaces.IAbsoluteURL` for site + root objects (:class:`zope.location.interfaces.IRoot`). + """ def __str__(self): context = self.context diff --git a/src/zope/traversing/browser/interfaces.py b/src/zope/traversing/browser/interfaces.py index 0ef4f62..df468ce 100644 --- a/src/zope/traversing/browser/interfaces.py +++ b/src/zope/traversing/browser/interfaces.py @@ -17,6 +17,12 @@ from zope.interface import Interface class IAbsoluteURL(Interface): + """ + An absolute URL. + + These are typically registered as adapters or multi-adapters + for objects. + """ def __unicode__(): """Returns the URL as a unicode string.""" @@ -39,6 +45,16 @@ class IAbsoluteURL(Interface): class IAbsoluteURLAPI(Interface): + """ + The api to compute absolute URLs of objects. + + Provided by :mod:`zope.traversing.browser.absoluteurl` + """ def absoluteURL(ob, request): - """Compute the absolute URL of an object """ + """ + Compute the absolute URL of an object. + + This should return an ASCII string by looking up an adapter + from `(ob, request)` to :class:`IAbsoluteURL` and then calling it. + """ diff --git a/src/zope/traversing/interfaces.py b/src/zope/traversing/interfaces.py index cd515ed..ad3b410 100644 --- a/src/zope/traversing/interfaces.py +++ b/src/zope/traversing/interfaces.py @@ -36,12 +36,12 @@ class ITraversable(Interface): """Get the next item on the path Should return the item corresponding to 'name' or raise - LocationError where appropriate. + :exc:`~zope.location.interfaces.LocationError` where appropriate. - 'name' is an ASCII string or Unicode object. - - 'furtherPath' is a list of names still to be traversed. This - method is allowed to change the contents of furtherPath. + :param str name: an ASCII string or Unicode object. + :param list furtherPath: is a list of names still to be + traversed. This method is allowed to change the contents + of furtherPath. """ @@ -62,31 +62,34 @@ class ITraverser(Interface): path begins with a '/', start at the root. Otherwise the path is relative to the current context. - If the object is not found, return 'default' argument. + If the object is not found, return *default* argument. """ class ITraversalAPI(Interface): - """Common API functions to ease traversal computations + """ + Common API functions to ease traversal computations. + + This is provided by :mod:`zope.traversing.api`. """ def joinPath(path, *args): - """Join the given relative paths to the given path. + """Join the given relative paths to the given *path*. - Returns a unicode path. + Returns a text (`unicode`) path. The path should be well-formed, and not end in a '/' unless it is the root path. It can be either a string (ascii only) or unicode. The positional arguments are relative paths to be added to the - path as new path segments. The path may be absolute or relative. + path as new path segments. The path may be absolute or relative. A segment may not start with a '/' because that would be confused with an absolute path. A segment may not end with a '/' because we do not allow '/' at the end of relative paths. A segment may - consist of . or .. to mean "the same place", or "the parent path" + consist of '.' or '..' to mean "the same place", or "the parent path" respectively. A '.' should be removed and a '..' should cause the - segment to the left to be removed. joinPath('/', '..') should + segment to the left to be removed. ``joinPath('/', '..')`` should raise an exception. """ @@ -99,40 +102,37 @@ class ITraversalAPI(Interface): """ def traverse(object, path, default=None, request=None): - """Traverse 'path' relative to the given object. - - 'path' is a string with path segments separated by '/'. - - 'request' is passed in when traversing from presentation code. This - allows paths like @@foo to work. - - Raises LocationError if path cannot be found - - Note: calling traverse with a path argument taken from an untrusted - source, such as an HTTP request form variable, is a bad idea. - It could allow a maliciously constructed request to call - code unexpectedly. - Consider using traverseName instead. + """Traverse *path* relative to the given object. + + :param str path: a string with path segments separated by '/'. + :keyword request: Passed in when traversing from + presentation code. This allows paths like "@@foo" to work. + :raises zope.location.interfaces.LocationError: if *path* cannot be found + + .. note:: Calling `traverse` with a path argument taken from an + untrusted source, such as an HTTP request form variable, + is a bad idea. It could allow a maliciously constructed + request to call code unexpectedly. Consider using + `traverseName` instead. """ def traverseName(obj, name, default=None, traversable=None, request=None): - """Traverse a single step 'name' relative to the given object. - - 'name' must be a string. '.' and '..' are treated specially, as well as - names starting with '@' or '+'. Otherwise 'name' will be treated as a - single path segment. + """Traverse a single step *name* relative to the given object. - You can explicitly pass in an ITraversable as the - 'traversable' argument. If you do not, the given object will - be adapted to ITraversable. + *name* must be a string. '.' and '..' are treated specially, + as well as names starting with '@' or '+'. Otherwise *name* + will be treated as a single path segment. - 'request' is passed in when traversing from presentation code. This - allows paths like @@foo to work. + You can explicitly pass in an `ITraversable` as the + *traversable* argument. If you do not, the given object will + be adapted to `ITraversable`. - Raises LocationError if path cannot be found and 'default' was - not provided. + *request* is passed in when traversing from presentation code. + This allows paths like "@@foo" to work. + :raises zope.location.interfaces.LocationError: if *path* cannot + be found and *default* was not provided. """ def getName(obj): @@ -142,25 +142,28 @@ class ITraversalAPI(Interface): def getParent(obj): """Returns the container the object was traversed via. - Returns None if the object is a containment root. - Raises TypeError if the object doesn't have enough context to get the - parent. + Returns `None` if the object is a containment root. + + :raises TypeError: if the object doesn't have enough context + to get the parent. """ def getParents(obj): - """Returns a list starting with the given object's parent followed by - each of its parents. + """ + Returns a list starting with the given object's parent + followed by each of its parents. - Raises a TypeError if the context doesn't go all the way down to - a containment root. + :raises TypeError: if the context doesn't go all the way down + to a containment root. """ def canonicalPath(path_or_object): - """Returns a canonical absolute unicode path for the path or object. + """ + Returns a canonical absolute unicode path for the path or object. Resolves segments that are '.' or '..'. - Raises ValueError if a badly formed path is given. + :raises ValueError: if a badly formed path is given. """ @@ -182,7 +185,11 @@ class IBeforeTraverseEvent(IObjectEvent): @implementer(IBeforeTraverseEvent) class BeforeTraverseEvent(ObjectEvent): - """An event which gets sent on publication traverse""" + """ + An event which gets sent on publication traverse. + + Default implementation of `IBeforeTraverseEvent`. + """ def __init__(self, ob, request): 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 diff --git a/src/zope/traversing/publicationtraverse.py b/src/zope/traversing/publicationtraverse.py index 603d0c6..efa4046 100644 --- a/src/zope/traversing/publicationtraverse.py +++ b/src/zope/traversing/publicationtraverse.py @@ -30,16 +30,19 @@ class PublicationTraverser(object): """Traversal used for publication. The significant differences from - zope.traversing.adapters.traversePathElement() are: + `zope.traversing.adapters.traversePathElement` are: - - Instead of adapting each traversed object to ITraversable, this - version multi-adapts (ob, request) to IPublishTraverse. + - Instead of adapting each traversed object to ITraversable, + this version multi-adapts (ob, request) to + `zope.publisher.interfaces.IPublishTraverse`. - - This version wraps a security proxy around each traversed object. + - This version wraps a security proxy around each traversed + object. - - This version raises NotFound rather than LocationError. + - This version raises `zope.publisher.interfaces.NotFound` + rather than `zope.location.interfaces.LocationError`. - - This version has a method, traverseRelativeURL(), that + - This version has a method, :meth:`traverseRelativeURL`, that supports "browserDefault" traversal. """ def proxy(self, ob): @@ -126,6 +129,9 @@ PublicationTraverse = PublicationTraverser class PublicationTraverserWithoutProxy(PublicationTraverse): + """ + A `PublicationTraverse` that does not add security proxies. + """ def proxy(self, ob): return ob diff --git a/src/zope/traversing/tests/test_namespacetrversal.py b/src/zope/traversing/tests/test_namespacetrversal.py index 0fdd4db..cfd2e31 100644 --- a/src/zope/traversing/tests/test_namespacetrversal.py +++ b/src/zope/traversing/tests/test_namespacetrversal.py @@ -27,6 +27,15 @@ from zope.component.testing import setUp, tearDown from zope.testing.renormalizing import RENormalizing +class TestSimpleHandler(unittest.TestCase): + + def test_constructor(self): + h = namespace.SimpleHandler(42) + self.assertEqual(h.context, 42) + + h = namespace.SimpleHandler(42, 43) + self.assertEqual(h.context, 42) + class TestFunctions(unittest.TestCase): def test_getResource_not_found(self): |