8 """ |
8 """ |
9 |
9 |
10 import re |
10 import re |
11 |
11 |
12 from django.http import Http404 |
12 from django.http import Http404 |
|
13 from django.conf import settings |
13 from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist |
14 from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist |
14 from django.utils.datastructures import MultiValueDict |
15 from django.utils.datastructures import MultiValueDict |
15 from django.utils.encoding import iri_to_uri, force_unicode, smart_str |
16 from django.utils.encoding import iri_to_uri, force_unicode, smart_str |
16 from django.utils.functional import memoize |
17 from django.utils.functional import memoize |
17 from django.utils.importlib import import_module |
18 from django.utils.importlib import import_module |
18 from django.utils.regex_helper import normalize |
19 from django.utils.regex_helper import normalize |
19 from django.utils.thread_support import currentThread |
20 from django.utils.thread_support import currentThread |
20 |
21 |
21 try: |
|
22 reversed |
|
23 except NameError: |
|
24 from django.utils.itercompat import reversed # Python 2.3 fallback |
|
25 from sets import Set as set |
|
26 |
|
27 _resolver_cache = {} # Maps URLconf modules to RegexURLResolver instances. |
22 _resolver_cache = {} # Maps URLconf modules to RegexURLResolver instances. |
28 _callable_cache = {} # Maps view and url pattern names to their view functions. |
23 _callable_cache = {} # Maps view and url pattern names to their view functions. |
29 |
24 |
30 # SCRIPT_NAME prefixes for each thread are stored here. If there's no entry for |
25 # SCRIPT_NAME prefixes for each thread are stored here. If there's no entry for |
31 # the current thread (which is the only one we ever access), it is assumed to |
26 # the current thread (which is the only one we ever access), it is assumed to |
32 # be empty. |
27 # be empty. |
33 _prefixes = {} |
28 _prefixes = {} |
|
29 |
|
30 # Overridden URLconfs for each thread are stored here. |
|
31 _urlconfs = {} |
34 |
32 |
35 class Resolver404(Http404): |
33 class Resolver404(Http404): |
36 pass |
34 pass |
37 |
35 |
38 class NoReverseMatch(Exception): |
36 class NoReverseMatch(Exception): |
129 return self._callback |
127 return self._callback |
130 try: |
128 try: |
131 self._callback = get_callable(self._callback_str) |
129 self._callback = get_callable(self._callback_str) |
132 except ImportError, e: |
130 except ImportError, e: |
133 mod_name, _ = get_mod_func(self._callback_str) |
131 mod_name, _ = get_mod_func(self._callback_str) |
134 raise ViewDoesNotExist, "Could not import %s. Error was: %s" % (mod_name, str(e)) |
132 raise ViewDoesNotExist("Could not import %s. Error was: %s" % (mod_name, str(e))) |
135 except AttributeError, e: |
133 except AttributeError, e: |
136 mod_name, func_name = get_mod_func(self._callback_str) |
134 mod_name, func_name = get_mod_func(self._callback_str) |
137 raise ViewDoesNotExist, "Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e)) |
135 raise ViewDoesNotExist("Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e))) |
138 return self._callback |
136 return self._callback |
139 callback = property(_get_callback) |
137 callback = property(_get_callback) |
140 |
138 |
141 class RegexURLResolver(object): |
139 class RegexURLResolver(object): |
142 def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, namespace=None): |
140 def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, namespace=None): |
228 sub_match_dict.update(self.default_kwargs) |
226 sub_match_dict.update(self.default_kwargs) |
229 for k, v in sub_match[2].iteritems(): |
227 for k, v in sub_match[2].iteritems(): |
230 sub_match_dict[smart_str(k)] = v |
228 sub_match_dict[smart_str(k)] = v |
231 return sub_match[0], sub_match[1], sub_match_dict |
229 return sub_match[0], sub_match[1], sub_match_dict |
232 tried.append(pattern.regex.pattern) |
230 tried.append(pattern.regex.pattern) |
233 raise Resolver404, {'tried': tried, 'path': new_path} |
231 raise Resolver404({'tried': tried, 'path': new_path}) |
234 raise Resolver404, {'path' : path} |
232 raise Resolver404({'path' : path}) |
235 |
233 |
236 def _get_urlconf_module(self): |
234 def _get_urlconf_module(self): |
237 try: |
235 try: |
238 return self._urlconf_module |
236 return self._urlconf_module |
239 except AttributeError: |
237 except AttributeError: |
244 def _get_url_patterns(self): |
242 def _get_url_patterns(self): |
245 patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module) |
243 patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module) |
246 try: |
244 try: |
247 iter(patterns) |
245 iter(patterns) |
248 except TypeError: |
246 except TypeError: |
249 raise ImproperlyConfigured("The included urlconf %s doesn't have any " |
247 raise ImproperlyConfigured("The included urlconf %s doesn't have any patterns in it" % self.urlconf_name) |
250 "patterns in it" % self.urlconf_name) |
|
251 return patterns |
248 return patterns |
252 url_patterns = property(_get_url_patterns) |
249 url_patterns = property(_get_url_patterns) |
253 |
250 |
254 def _resolve_special(self, view_type): |
251 def _resolve_special(self, view_type): |
255 callback = getattr(self.urlconf_module, 'handler%s' % view_type) |
252 callback = getattr(self.urlconf_module, 'handler%s' % view_type) |
256 mod_name, func_name = get_mod_func(callback) |
253 try: |
257 try: |
254 return get_callable(callback), {} |
258 return getattr(import_module(mod_name), func_name), {} |
|
259 except (ImportError, AttributeError), e: |
255 except (ImportError, AttributeError), e: |
260 raise ViewDoesNotExist, "Tried %s. Error was: %s" % (callback, str(e)) |
256 raise ViewDoesNotExist("Tried %s. Error was: %s" % (callback, str(e))) |
261 |
257 |
262 def resolve404(self): |
258 def resolve404(self): |
263 return self._resolve_special('404') |
259 return self._resolve_special('404') |
264 |
260 |
265 def resolve500(self): |
261 def resolve500(self): |
298 lookup_view_s = lookup_view |
294 lookup_view_s = lookup_view |
299 raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword " |
295 raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword " |
300 "arguments '%s' not found." % (lookup_view_s, args, kwargs)) |
296 "arguments '%s' not found." % (lookup_view_s, args, kwargs)) |
301 |
297 |
302 def resolve(path, urlconf=None): |
298 def resolve(path, urlconf=None): |
|
299 if urlconf is None: |
|
300 urlconf = get_urlconf() |
303 return get_resolver(urlconf).resolve(path) |
301 return get_resolver(urlconf).resolve(path) |
304 |
302 |
305 def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, current_app=None): |
303 def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, current_app=None): |
|
304 if urlconf is None: |
|
305 urlconf = get_urlconf() |
306 resolver = get_resolver(urlconf) |
306 resolver = get_resolver(urlconf) |
307 args = args or [] |
307 args = args or [] |
308 kwargs = kwargs or {} |
308 kwargs = kwargs or {} |
309 |
309 |
310 if prefix is None: |
310 if prefix is None: |
369 wishes to construct their own URLs manually (although accessing the request |
369 wishes to construct their own URLs manually (although accessing the request |
370 instance is normally going to be a lot cleaner). |
370 instance is normally going to be a lot cleaner). |
371 """ |
371 """ |
372 return _prefixes.get(currentThread(), u'/') |
372 return _prefixes.get(currentThread(), u'/') |
373 |
373 |
|
374 def set_urlconf(urlconf_name): |
|
375 """ |
|
376 Sets the URLconf for the current thread (overriding the default one in |
|
377 settings). Set to None to revert back to the default. |
|
378 """ |
|
379 thread = currentThread() |
|
380 if urlconf_name: |
|
381 _urlconfs[thread] = urlconf_name |
|
382 else: |
|
383 # faster than wrapping in a try/except |
|
384 if thread in _urlconfs: |
|
385 del _urlconfs[thread] |
|
386 |
|
387 def get_urlconf(default=None): |
|
388 """ |
|
389 Returns the root URLconf to use for the current thread if it has been |
|
390 changed from the default one. |
|
391 """ |
|
392 thread = currentThread() |
|
393 if thread in _urlconfs: |
|
394 return _urlconfs[thread] |
|
395 return default |