diff options
Diffstat (limited to 'repoze/bfg/traversal.py')
| -rw-r--r-- | repoze/bfg/traversal.py | 81 |
1 files changed, 46 insertions, 35 deletions
diff --git a/repoze/bfg/traversal.py b/repoze/bfg/traversal.py index e330a14eb..02b6589ac 100644 --- a/repoze/bfg/traversal.py +++ b/repoze/bfg/traversal.py @@ -108,7 +108,7 @@ def find_model(model, path): else: path = '' - if path.startswith('/'): + if path and path[0] == '/': model = find_root(model) ob, name, _ = ITraverserFactory(model)({'PATH_INFO':path}) @@ -165,10 +165,11 @@ def model_path(model, *elements): effectively replaced with a leading ``/``) when the path is generated. """ - path = [ quote_path_segment(name) for name in - model_path_tuple(model, *elements) ] - path = '/'.join(path) or '/' - return path + path_list = _model_path_list(model, *elements) + if path_list: + path_list = [ quote_path_segment(name) for name in path_list ] + return '/' + '/'.join(path_list) + return '/' def model_path_tuple(model, *elements): """ @@ -204,18 +205,20 @@ def model_path_tuple(model, *elements): always be ignored (and effectively replaced with ``''``) when the path is generated. """ - path = [] - for location in lineage(model): - path.append(location.__name__) + path = _model_path_list(model, *elements) + if path: + return ('',) + tuple(path) + return ('',) - # replace root __name__ with the empty string - path.pop() - path.append('') +def _model_path_list(model, *elements): + """ Implementation detail shared by model_path and model_path_tuple """ + lpath = reversed(list(lineage(model))[:-1]) + path = [ location.__name__ for location in lpath ] - path.reverse() - path.extend(elements) - return tuple(path) + if elements: + path = path + list(elements) + return path def virtual_root(model, request): """ @@ -329,14 +332,26 @@ def quote_path_segment(segment): URL-quoted using Python's ``urllib.quote``. If the segment passed in is not a string or unicode object, an error will be raised. The return value of ``quote_path_segment`` is always a string, - never Unicode.""" + never Unicode. + + .. note:: The return value for each segment passed to this + function is cached in a module-scope dictionary for + speed: the cached version is returned when possible + rather than recomputing the quoted version. No cache + emptying is ever done for the lifetime of an + application, however. If you pass arbitrary + user-supplied strings to this function (as opposed to + some bounded set of values from a 'working set' known to + your application), it may become a memory leak. + """ # 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(segment) - if result is None: + try: + return _segment_cache[segment] + except KeyError: if segment.__class__ is unicode: # isinstance slighly slower (~15%) result = _url_quote(segment.encode('utf-8')) else: @@ -344,7 +359,7 @@ def quote_path_segment(segment): # we don't need a lock to mutate _segment_cache, as the below # will generate exactly one Python bytecode (STORE_SUBSCR) _segment_cache[segment] = result - return result + return result _marker = object() @@ -417,26 +432,22 @@ class TraversalContextURL(object): """ Generate a URL based on the lineage of a model obtained via traversal. If any model in the context lineage has a unicode name, it will be converted to a UTF-8 string before - being attached to the URL. When composing the path based on - the model lineage, empty names in the model graph are ignored. - If a ``HTTP_X_VHM_ROOT`` key is present in the WSGI - environment, its value will be treated as a 'virtual root - path': the path of the URL generated by this will be - left-stripped of this virtual root path value. + being attached to the URL. If a ``HTTP_X_VHM_ROOT`` key is + present in the WSGI environment, its value will be treated as + a 'virtual root path': the path of the URL generated by this + will be left-stripped of this virtual root path value. """ - rpath = [] - for location in lineage(self.context): - name = location.__name__ - if name: - rpath.append(quote_path_segment(name)) - if rpath: - path = '/' + '/'.join(reversed(rpath)) + '/' - else: - path = '/' + path = model_path(self.context) + if path != '/': + path = path + '/' request = self.request + # if the path starts with the virtual root path, trim it out - vroot_path = request.environ.get(self.vroot_varname) - if vroot_path is not None: + try: + vroot_path = request.environ[self.vroot_varname] + except KeyError: + pass + else: if path.startswith(vroot_path): path = path[len(vroot_path):] |
