From a55598cbdd5e8ab58bb58e2178ccbe155526d735 Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Fri, 5 Aug 2005 20:50:19 +0000 Subject: [PATCH] Refactored the internals of URL parsing to use less code git-svn-id: http://code.djangoproject.com/svn/django/trunk@411 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/conf/urls/defaults.py | 4 +- django/core/handlers/base.py | 15 +++----- django/core/urlresolvers.py | 71 +++++++++++++++--------------------- 3 files changed, 38 insertions(+), 52 deletions(-) diff --git a/django/conf/urls/defaults.py b/django/conf/urls/defaults.py index 9971dd2eb7a..07611c51478 100644 --- a/django/conf/urls/defaults.py +++ b/django/conf/urls/defaults.py @@ -1,4 +1,4 @@ -from django.core.urlresolvers import RegexURLMultiplePattern, RegexURLPattern +from django.core.urlresolvers import RegexURLPattern, RegexURLResolver __all__ = ['handler404', 'handler500', 'include', 'patterns'] @@ -11,7 +11,7 @@ def patterns(prefix, *tuples): pattern_list = [] for t in tuples: if type(t[1]) == list: - pattern_list.append(RegexURLMultiplePattern(t[0], t[1][0])) + pattern_list.append(RegexURLResolver(t[0], t[1][0])) else: pattern_list.append(RegexURLPattern(t[0], prefix and (prefix + '.' + t[1]) or t[1], *t[2:])) return pattern_list diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py index 5a5bf69d2f3..115b6ad56f5 100644 --- a/django/core/handlers/base.py +++ b/django/core/handlers/base.py @@ -51,8 +51,7 @@ class BaseHandler: if response: return response - conf_module = __import__(ROOT_URLCONF, '', '', ['']) - resolver = urlresolvers.RegexURLResolver(conf_module.urlpatterns) + resolver = urlresolvers.RegexURLResolver(r'^/', ROOT_URLCONF) try: callback, param_dict = resolver.resolve(path) # Apply view middleware @@ -65,8 +64,7 @@ class BaseHandler: if DEBUG: return self.get_technical_error_response(is404=True) else: - resolver = urlresolvers.Error404Resolver(conf_module.handler404) - callback, param_dict = resolver.resolve() + callback, param_dict = resolver.resolve404() return callback(request, **param_dict) except db.DatabaseError: db.db.rollback() @@ -76,7 +74,7 @@ class BaseHandler: subject = 'Database error (%s IP)' % (request.META['REMOTE_ADDR'] in INTERNAL_IPS and 'internal' or 'EXTERNAL') message = "%s\n\n%s" % (self._get_traceback(), request) mail_admins(subject, message, fail_silently=True) - return self.get_friendly_error_response(request, conf_module) + return self.get_friendly_error_response(request, resolver) except exceptions.PermissionDenied: return httpwrappers.HttpResponseForbidden('

Permission denied

') except: # Handle everything else, including SuspiciousOperation, etc. @@ -90,16 +88,15 @@ class BaseHandler: request_repr = "Request repr() unavailable" message = "%s\n\n%s" % (self._get_traceback(), request_repr) mail_admins(subject, message, fail_silently=True) - return self.get_friendly_error_response(request, conf_module) + return self.get_friendly_error_response(request, resolver) - def get_friendly_error_response(self, request, conf_module): + def get_friendly_error_response(self, request, resolver): """ Returns an HttpResponse that displays a PUBLIC error message for a fundamental database or coding error. """ from django.core import urlresolvers - resolver = urlresolvers.Error404Resolver(conf_module.handler500) - callback, param_dict = resolver.resolve() + callback, param_dict = resolver.resolve500() return callback(request, **param_dict) def get_technical_error_response(self, is404=False): diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py index b5376336466..50f93361719 100644 --- a/django/core/urlresolvers.py +++ b/django/core/urlresolvers.py @@ -18,13 +18,14 @@ def get_mod_func(callback): class RegexURLPattern: def __init__(self, regex, callback, default_args=None): - self.regex = re.compile(regex) + # regex is a string representing a regular expression. # callback is something like 'foo.views.news.stories.story_detail', # which represents the path to a module and a view function name. + self.regex = re.compile(regex) self.callback = callback self.default_args = default_args or {} - def search(self, path): + def resolve(self, path): match = self.regex.search(path) if match: args = dict(match.groupdict(), **self.default_args) @@ -43,56 +44,44 @@ class RegexURLPattern: except AttributeError, e: raise ViewDoesNotExist, "Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e)) -class RegexURLMultiplePattern: - def __init__(self, regex, urlconf_module): +class RegexURLResolver: + def __init__(self, regex, urlconf_name): + # regex is a string representing a regular expression. + # urlconf_name is a string representing the module containing urlconfs. self.regex = re.compile(regex) - # urlconf_module is a string representing the module containing urlconfs. - self.urlconf_module = urlconf_module + self.urlconf_name = urlconf_name - def search(self, path): + def resolve(self, path): match = self.regex.search(path) if match: new_path = path[match.end():] - try: # Lazily load self.url_patterns. - self.url_patterns - except AttributeError: - self.url_patterns = self.get_url_patterns() for pattern in self.url_patterns: - sub_match = pattern.search(new_path) + sub_match = pattern.resolve(new_path) if sub_match: return sub_match + # None of the regexes matched, so raise a 404. + raise Http404, "Tried all URL patterns but didn't find a match for %r" % path - def get_url_patterns(self): - return __import__(self.urlconf_module, '', '', ['']).urlpatterns + def _get_urlconf_module(self): + self.urlconf_module = __import__(self.urlconf_name, '', '', ['']) + return self.urlconf_module + urlconf_module = property(_get_urlconf_module) -class RegexURLResolver: - def __init__(self, url_patterns): - # url_patterns is a list of RegexURLPattern or RegexURLMultiplePattern objects. - self.url_patterns = url_patterns + def _get_url_patterns(self): + self.url_patterns = self.urlconf_module.urlpatterns + return self.url_patterns + url_patterns = property(_get_url_patterns) - def resolve(self, app_path): - # app_path is the full requested Web path. This is assumed to have a - # leading slash but doesn't necessarily have a trailing slash. - # Examples: - # "/news/2005/may/" - # "/news/" - # "/polls/latest" - # A home (root) page is represented by "/". - app_path = app_path[1:] # Trim leading slash. - for pattern in self.url_patterns: - match = pattern.search(app_path) - if match: - return match - # None of the regexes matched, so raise a 404. - raise Http404, "Tried all URL patterns but didn't find a match for %r" % app_path - -class Error404Resolver: - def __init__(self, callback): - self.callback = callback - - def resolve(self): - mod_name, func_name = get_mod_func(self.callback) + def _resolve_special(self, view_type): + callback = getattr(self.urlconf_module, 'handler%s' % view_type) + mod_name, func_name = get_mod_func(callback) try: return getattr(__import__(mod_name, '', '', ['']), func_name), {} except (ImportError, AttributeError), e: - raise ViewDoesNotExist, "Tried %s. Error was: %s" % (self.callback, str(e)) + raise ViewDoesNotExist, "Tried %s. Error was: %s" % (callback, str(e)) + + def resolve404(self): + return self._resolve_special('404') + + def resolve500(self): + return self._resolve_special('500')