diff options
| author | Chris McDonough <chrism@agendaless.com> | 2009-01-05 23:36:27 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2009-01-05 23:36:27 +0000 |
| commit | 7ae0c2522bbb2b026bc0370dbb1b1608f146137d (patch) | |
| tree | d46aa3e872eaf70d900af720b7458efd2ed0a189 /repoze/bfg/traversal.py | |
| parent | 96d8a517bf195a1ce0787e6ec16c3db82bef98f5 (diff) | |
| download | pyramid-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.py | 145 |
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) |
