052aa55d34
There are no longer two separate projects living inside the horizon repository. There is a single project now with a single setup.py, single README, etc. The openstack-dashboard/dashboard django project is now named "openstack_dashboard" and lives as an example project in the topmost horizon directory. The "horizon/horizon" directory has been bumped up a level and now is directly on the path when the root horizon directory is on your python path. Javascript media which the horizon module directly relies upon now ships in the horizon/static dir rather than openstack-dashboard/dashboard/static. All the corresponding setup, installation, build, and env scripts have been updated accordingly. Implements blueprint unified-packaging. Change-Id: Ieed8e3c777432cd046c3e0298869a9428756ab62
184 lines
8.3 KiB
Python
184 lines
8.3 KiB
Python
"""
|
|
Bugfix for issue #15900: https://code.djangoproject.com/ticket/15900.
|
|
|
|
This code is largely reproduced from
|
|
https://code.djangoproject.com/browser/django/trunk/django/core/urlresolvers.py
|
|
and is the work of Django's authors:
|
|
https://code.djangoproject.com/browser/django/trunk/AUTHORS
|
|
|
|
It is licensed under Django's BSD license, available here:
|
|
https://code.djangoproject.com/browser/django/trunk/LICENSE
|
|
|
|
To use, simply import this code in your project's root URLconf file before
|
|
defining any URL patterns.
|
|
"""
|
|
|
|
from django.core import urlresolvers
|
|
|
|
if not hasattr(urlresolvers.RegexURLResolver, "_reverse_with_prefix"):
|
|
import re
|
|
|
|
from django.conf import urls
|
|
from django.utils.datastructures import MultiValueDict
|
|
from django.utils.encoding import iri_to_uri, force_unicode
|
|
from django.utils.regex_helper import normalize
|
|
|
|
def _populate(self):
|
|
lookups = MultiValueDict()
|
|
namespaces = {}
|
|
apps = {}
|
|
for pattern in reversed(self.url_patterns):
|
|
p_pattern = pattern.regex.pattern
|
|
if p_pattern.startswith('^'):
|
|
p_pattern = p_pattern[1:]
|
|
if isinstance(pattern, urlresolvers.RegexURLResolver):
|
|
if pattern.namespace:
|
|
namespaces[pattern.namespace] = (p_pattern, pattern)
|
|
if pattern.app_name:
|
|
apps.setdefault(pattern.app_name, []) \
|
|
.append(pattern.namespace)
|
|
else:
|
|
parent = normalize(pattern.regex.pattern)
|
|
for name in pattern.reverse_dict:
|
|
for matches, pat, defaults in \
|
|
pattern.reverse_dict.getlist(name):
|
|
new_matches = []
|
|
for piece, p_args in parent:
|
|
vals = [(piece + suffix, p_args + args) for \
|
|
(suffix, args) in matches]
|
|
new_matches.extend(vals)
|
|
lookup_list = (new_matches, p_pattern + pat,
|
|
dict(defaults,
|
|
**pattern.default_kwargs))
|
|
lookups.appendlist(name, lookup_list)
|
|
for namespace, (prefix, sub_pattern) in \
|
|
pattern.namespace_dict.items():
|
|
namespace_vals = (p_pattern + prefix, sub_pattern)
|
|
namespaces[namespace] = namespace_vals
|
|
for app_name, namespace_list in pattern.app_dict.items():
|
|
apps.setdefault(app_name, []).extend(namespace_list)
|
|
else:
|
|
bits = normalize(p_pattern)
|
|
lookup_list = (bits, p_pattern, pattern.default_args)
|
|
lookups.appendlist(pattern.callback, lookup_list)
|
|
if pattern.name is not None:
|
|
lookup_list = (bits, p_pattern, pattern.default_args)
|
|
lookups.appendlist(pattern.name, lookup_list)
|
|
self._reverse_dict = lookups
|
|
self._namespace_dict = namespaces
|
|
self._app_dict = apps
|
|
|
|
def resolver_reverse(self, lookup_view, *args, **kwargs):
|
|
return self._reverse_with_prefix(lookup_view, '', *args, **kwargs)
|
|
|
|
def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs):
|
|
if args and kwargs:
|
|
raise ValueError("Don't mix *args and **kwargs in call to "
|
|
"reverse()!")
|
|
try:
|
|
lookup_view = urlresolvers.get_callable(lookup_view, True)
|
|
except (ImportError, AttributeError), e:
|
|
raise urlresolvers.NoReverseMatch("Error importing '%s': %s."
|
|
% (lookup_view, e))
|
|
possibilities = self.reverse_dict.getlist(lookup_view)
|
|
prefix_norm, prefix_args = normalize(_prefix)[0]
|
|
for possibility, pattern, defaults in possibilities:
|
|
for result, params in possibility:
|
|
if args:
|
|
if len(args) != len(params) + len(prefix_args):
|
|
continue
|
|
unicode_args = [force_unicode(val) for val in args]
|
|
candidate = (prefix_norm + result) \
|
|
% dict(zip(prefix_args + params, unicode_args))
|
|
else:
|
|
if set(kwargs.keys() + defaults.keys()) != \
|
|
set(params + defaults.keys() + prefix_args):
|
|
continue
|
|
matches = True
|
|
for k, v in defaults.items():
|
|
if kwargs.get(k, v) != v:
|
|
matches = False
|
|
break
|
|
if not matches:
|
|
continue
|
|
unicode_kwargs = dict([(k, force_unicode(v)) for \
|
|
(k, v) in kwargs.items()])
|
|
candidate = (prefix_norm + result) % unicode_kwargs
|
|
if re.search(u'^%s%s' % (_prefix, pattern),
|
|
candidate, re.UNICODE):
|
|
return candidate
|
|
# lookup_view can be URL label, or dotted path, or callable, Any of
|
|
# these can be passed in at the top, but callables are not friendly in
|
|
# error messages.
|
|
m = getattr(lookup_view, '__module__', None)
|
|
n = getattr(lookup_view, '__name__', None)
|
|
if m is not None and n is not None:
|
|
lookup_view_s = "%s.%s" % (m, n)
|
|
else:
|
|
lookup_view_s = lookup_view
|
|
raise urlresolvers.NoReverseMatch("Reverse for '%s' with "
|
|
"arguments '%s' and keyword "
|
|
"arguments '%s' not found."
|
|
% (lookup_view_s, args, kwargs))
|
|
|
|
def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None,
|
|
current_app=None):
|
|
if urlconf is None:
|
|
urlconf = urlresolvers.get_urlconf()
|
|
resolver = urlresolvers.get_resolver(urlconf)
|
|
args = args or []
|
|
kwargs = kwargs or {}
|
|
|
|
if prefix is None:
|
|
prefix = urlresolvers.get_script_prefix()
|
|
|
|
if not isinstance(viewname, basestring):
|
|
view = viewname
|
|
else:
|
|
parts = viewname.split(':')
|
|
parts.reverse()
|
|
view = parts[0]
|
|
path = parts[1:]
|
|
|
|
resolved_path = []
|
|
while path:
|
|
ns = path.pop()
|
|
|
|
# Lookup the name to see if it could be an app identifier
|
|
try:
|
|
app_list = resolver.app_dict[ns]
|
|
# Yes! Path part matches an app in the current Resolver
|
|
if current_app and current_app in app_list:
|
|
# If we are reversing for a particular app,
|
|
# use that namespace
|
|
ns = current_app
|
|
elif ns not in app_list:
|
|
# The name isn't shared by one of the instances
|
|
# (i.e., the default) so just pick the first instance
|
|
# as the default.
|
|
ns = app_list[0]
|
|
except KeyError:
|
|
pass
|
|
|
|
try:
|
|
extra, resolver = resolver.namespace_dict[ns]
|
|
resolved_path.append(ns)
|
|
prefix = prefix + extra
|
|
except KeyError, key:
|
|
if resolved_path:
|
|
raise urlresolvers.NoReverseMatch("%s is not a "
|
|
"registered namespace inside %s'"
|
|
% (key, ':'.join(resolved_path)))
|
|
else:
|
|
raise urlresolvers.NoReverseMatch("%s is not a "
|
|
"registered "
|
|
"namespace" % key)
|
|
|
|
return iri_to_uri(resolver._reverse_with_prefix(view, prefix,
|
|
*args, **kwargs))
|
|
|
|
urlresolvers.RegexURLResolver._populate = _populate
|
|
urlresolvers.RegexURLResolver.reverse = resolver_reverse
|
|
urlresolvers.RegexURLResolver._reverse_with_prefix = _reverse_with_prefix
|
|
urlresolvers.reverse = reverse
|