summaryrefslogtreecommitdiff
path: root/repoze/bfg/traversal.py
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-01-05 23:36:27 +0000
committerChris McDonough <chrism@agendaless.com>2009-01-05 23:36:27 +0000
commit7ae0c2522bbb2b026bc0370dbb1b1608f146137d (patch)
treed46aa3e872eaf70d900af720b7458efd2ed0a189 /repoze/bfg/traversal.py
parent96d8a517bf195a1ce0787e6ec16c3db82bef98f5 (diff)
downloadpyramid-7ae0c2522bbb2b026bc0370dbb1b1608f146137d.tar.gz
pyramid-7ae0c2522bbb2b026bc0370dbb1b1608f146137d.tar.bz2
pyramid-7ae0c2522bbb2b026bc0370dbb1b1608f146137d.zip
New Modules
- A new module ``repoze.bfg.url`` has been added. It contains the ``model_url`` API (moved from ``repoze.bfg.traversal``) and an implementation of ``urlencode`` (like Python's ``urllib.urlencode``) which can handle Unicode keys and values in parameters to the ``query`` argument. Deprecations - The ``model_url`` function has been moved from ``repoze.bfg.traversal`` into ``repoze.bfg.url``. It can still be imported from ``repoze.bfg.traversal`` but an import from ``repoze.bfg.traversal`` will emit a DeprecationWarning. Features - The ``repoze.bfg.url.model_url`` API (nee' ``repoze.bfg.traversal.model_url``) now accepts and honors a keyword argument named ``query``. The value of this argument will be used to compose a query string, which will be attached to the generated URL before it is returned. See the API docs (in the docs directory or `on the web <http://static.repoze.org/bfgdocs>`_) for more information.
Diffstat (limited to 'repoze/bfg/traversal.py')
-rw-r--r--repoze/bfg/traversal.py145
1 files changed, 31 insertions, 114 deletions
diff --git a/repoze/bfg/traversal.py b/repoze/bfg/traversal.py
index 3c6756de1..e2e31fdfa 100644
--- a/repoze/bfg/traversal.py
+++ b/repoze/bfg/traversal.py
@@ -2,17 +2,26 @@ import re
import urllib
from zope.component import queryUtility
+from zope.deferredimport import deprecated
from zope.interface import classProvides
from zope.interface import implements
from repoze.bfg.location import LocationProxy
from repoze.bfg.location import lineage
+from repoze.bfg.url import urlencode
+
from repoze.bfg.interfaces import ILocation
from repoze.bfg.interfaces import ITraverser
from repoze.bfg.interfaces import ITraverserFactory
from repoze.bfg.interfaces import ISettings
+deprecated(
+ "('from repoze.bfg.traversal import model_url' is now "
+ "deprecated; instead use 'from repoze.bfg.url import model_url')",
+ model_url = "repoze.bfg.url:model_url",
+ )
+
def split_path(path):
while path.startswith('/'):
path = path[1:]
@@ -112,83 +121,36 @@ def find_model(model, path):
return ob
def find_interface(model, interface):
- """ Return the first object found which provides the interface
+ """
+ Return the first object found which provides the interface
``interface`` in the parent chain of ``model`` or ``None`` if no
object providing ``interface`` can be found in the parent chain.
- The ``model`` passed in should be :term:`location`-aware."""
+ The ``model`` passed in should be :term:`location`-aware.
+ """
for location in lineage(model):
if interface.providedBy(location):
return location
-_segment_cache = {}
-
-def _urlsegment(s):
- """ The bit of this code that deals with ``_segment_cache`` is an
- optimization: we cache all the computation of URL path segments in
- this module-scope dictionary with the original string (or unicode
- value) as the key, so we can look it up later without needing to
- reencode or re-url-quote it """
- result = _segment_cache.get(s)
- if result is None:
- if s.__class__ is unicode: # isinstance slighly slower (~15%)
- result = _url_quote(s.encode('utf-8'))
- else:
- result = _url_quote(s)
- # we don't need a lock to mutate _segment_cache, as the below
- # will generate exactly one Python bytecode (STORE_SUBSCR)
- _segment_cache[s] = result
- return result
-
-def model_url(model, request, *elements):
- """ Return a string representing the absolute URL of the model
- object based on the ``wsgi.url_scheme``, ``HTTP_HOST`` or
- ``SERVER_NAME`` in the request, plus any ``SCRIPT_NAME``. If any
- model in the lineage has a unicode name, it will be converted to
- UTF-8 before being attached to the URL. Empty names in the model
- graph are ignored.
-
- Any positional arguments passed in as ``elements`` must be strings
- or unicode objects. These will be joined by slashes and appended
- to the model URL. Each of the elements passed in is URL-quoted
- before being appended; if any element is unicode, it will
- converted to a UTF-8 bytestring before being URL-quoted.
-
- If ``elements`` are not used, the model URL will end with a
- trailing slash. If ``elements`` are used, the generated URL will
- *not* end in trailing a slash.
-
- The ``model`` passed in must be :term:`location`-aware. The
- overall result of this function is always a string (never
- unicode). """
- rpath = []
- for location in lineage(model):
- name = location.__name__
- if name:
- rpath.append(_urlsegment(name))
- prefix = '/'.join(reversed(rpath))
- suffix = '/'.join([_urlsegment(s) for s in elements])
- path = '/'.join([prefix, suffix])
- if not path.startswith('/'):
- path = '/' + path
- app_url = request.application_url # never ends in a slash
- return app_url + path
-
def model_path(model, *elements):
""" Return a string representing the absolute path of the model
- object based on its position in the model graph, e.g
- ``/foo/bar``. Any positional arguments passed in as ``elements``
- will be joined by slashes and appended to the generated path. The
- ``model`` passed in must be :term:`location`-aware.
-
- Note that this function is not equivalent to ``model_url``:
- individual segments in the path are not quoted in any way. Thus,
- to ensure that this function generates paths that can later be
- resolved to models via ``find_model``, you should ensure that your
- model names do not contain any slashes.
-
- Note also that the path returned may be a Unicode string if any
- model name in the model graph is Unicode; otherwise it will be a
- byte-string."""
+ object based on its position in the model graph, e.g ``/foo/bar``.
+ This function is the inverse of ``find_model``: it can be used to
+ generate paths that can later be resolved via ``find_model``. Any
+ positional arguments passed in as ``elements`` will be joined by
+ slashes and appended to the generated path. The ``model`` passed
+ in must be :term:`location`-aware.
+
+ Note that the way this function treats path segments is not
+ equivalent to how the ``repoze.bfg.url.model_url`` API treats path
+ segments: individual segments that make up the path are not quoted
+ in any way. Thus, to ensure that this function generates paths
+ that can later be resolved to models via ``find_model``, you
+ should ensure that your model __name__ attributes do not contain
+ any forward slashes.
+
+ The path returned may be a unicode object if any model name in the
+ model graph is a unicode object; otherwise it will be a string.
+ """
rpath = []
for location in lineage(model):
if location.__name__:
@@ -199,48 +161,3 @@ def model_path(model, *elements):
path = '/'.join([path, suffix])
return path
-always_safe = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'
- 'abcdefghijklmnopqrstuvwxyz'
- '0123456789' '_.-')
-_safemaps = {}
-_must_quote = {}
-
-def _url_quote(s, safe = '/'):
- """quote('abc def') -> 'abc%20def'
-
- Faster version of Python stdlib urllib.quote. See
- http://bugs.python.org/issue1285086 for more information.
-
- Each part of a URL, e.g. the path info, the query, etc., has a
- different set of reserved characters that must be quoted.
-
- RFC 2396 Uniform Resource Identifiers (URI): Generic Syntax lists
- the following reserved characters.
-
- reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
- "$" | ","
-
- Each of these characters is reserved in some component of a URL,
- but not necessarily in all of them.
-
- By default, the quote function is intended for quoting the path
- section of a URL. Thus, it will not encode '/'. This character
- is reserved, but in typical usage the quote function is being
- called on a path where the existing slash characters are used as
- reserved characters.
- """
- cachekey = (safe, always_safe)
- try:
- safe_map = _safemaps[cachekey]
- if not _must_quote[cachekey].search(s):
- return s
- except KeyError:
- safe += always_safe
- _must_quote[cachekey] = re.compile(r'[^%s]' % safe)
- safe_map = {}
- for i in range(256):
- c = chr(i)
- safe_map[c] = (c in safe) and c or ('%%%02X' % i)
- _safemaps[cachekey] = safe_map
- res = map(safe_map.__getitem__, s)
- return ''.join(res)