Kaydet (Commit) b4cdf4d1 authored tarafından Jannis Leidel's avatar Jannis Leidel

Fixed #10802 -- Handle ImportErrors and AttributeErrors gracefully when raised…

Fixed #10802 -- Handle ImportErrors and AttributeErrors gracefully when raised by the URL resolver system during startup. Many thanks, IonelMaries and Bas Peschier.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16420 bcc190cf-cafb-0310-a4f2-bffc1f526a37
üst 30e84263
...@@ -16,6 +16,7 @@ from django.utils.datastructures import MultiValueDict ...@@ -16,6 +16,7 @@ from django.utils.datastructures import MultiValueDict
from django.utils.encoding import iri_to_uri, force_unicode, smart_str from django.utils.encoding import iri_to_uri, force_unicode, smart_str
from django.utils.functional import memoize, lazy from django.utils.functional import memoize, lazy
from django.utils.importlib import import_module from django.utils.importlib import import_module
from django.utils.module_loading import module_has_submodule
from django.utils.regex_helper import normalize from django.utils.regex_helper import normalize
from django.utils.translation import get_language from django.utils.translation import get_language
...@@ -84,19 +85,28 @@ def get_callable(lookup_view, can_fail=False): ...@@ -84,19 +85,28 @@ def get_callable(lookup_view, can_fail=False):
during the import fail and the string is returned. during the import fail and the string is returned.
""" """
if not callable(lookup_view): if not callable(lookup_view):
mod_name, func_name = get_mod_func(lookup_view)
try: try:
# Bail early for non-ASCII strings (they can't be functions).
lookup_view = lookup_view.encode('ascii')
mod_name, func_name = get_mod_func(lookup_view)
if func_name != '': if func_name != '':
lookup_view = getattr(import_module(mod_name), func_name) lookup_view = getattr(import_module(mod_name), func_name)
if not callable(lookup_view): if not callable(lookup_view):
raise AttributeError("'%s.%s' is not a callable." % (mod_name, func_name)) raise ViewDoesNotExist(
except (ImportError, AttributeError): "Could not import %s.%s. View is not callable."
% (mod_name, func_name))
except AttributeError:
if not can_fail:
raise ViewDoesNotExist(
"Could not import %s. View does not exist in module %s."
% (lookup_view, mod_name))
except ImportError:
ownermod, submod = get_mod_func(mod_name)
if (not can_fail and submod != '' and
not module_has_submodule(import_module(ownermod), submod)):
raise ViewDoesNotExist(
"Could not import %s. Owning module %s does not exist."
% (lookup_view, mod_name))
if not can_fail: if not can_fail:
raise raise
except UnicodeEncodeError:
pass
return lookup_view return lookup_view
get_callable = memoize(get_callable, _callable_cache, 1) get_callable = memoize(get_callable, _callable_cache, 1)
...@@ -192,14 +202,8 @@ class RegexURLPattern(LocaleRegexProvider): ...@@ -192,14 +202,8 @@ class RegexURLPattern(LocaleRegexProvider):
def callback(self): def callback(self):
if self._callback is not None: if self._callback is not None:
return self._callback return self._callback
try:
self._callback = get_callable(self._callback_str) self._callback = get_callable(self._callback_str)
except ImportError, e:
mod_name, _ = get_mod_func(self._callback_str)
raise ViewDoesNotExist("Could not import %s. Error was: %s" % (mod_name, str(e)))
except AttributeError, e:
mod_name, func_name = get_mod_func(self._callback_str)
raise ViewDoesNotExist("Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e)))
return self._callback return self._callback
class RegexURLResolver(LocaleRegexProvider): class RegexURLResolver(LocaleRegexProvider):
...@@ -325,10 +329,7 @@ class RegexURLResolver(LocaleRegexProvider): ...@@ -325,10 +329,7 @@ class RegexURLResolver(LocaleRegexProvider):
# Lazy import, since urls.defaults imports this file # Lazy import, since urls.defaults imports this file
from django.conf.urls import defaults from django.conf.urls import defaults
callback = getattr(defaults, 'handler%s' % view_type) callback = getattr(defaults, 'handler%s' % view_type)
try: return get_callable(callback), {}
return get_callable(callback), {}
except (ImportError, AttributeError), e:
raise ViewDoesNotExist("Tried %s. Error was: %s" % (callback, str(e)))
def resolve404(self): def resolve404(self):
return self._resolve_special('404') return self._resolve_special('404')
......
from django.conf.urls.defaults import patterns, url
urlpatterns = patterns('',
# View has erroneous import
url(r'erroneous_inner/$', 'regressiontests.urlpatterns_reverse.views.erroneous_view'),
# Module has erroneous import
url(r'erroneous_outer/$', 'regressiontests.urlpatterns_reverse.erroneous_views_module.erroneous_view'),
# View does not exist
url(r'missing_inner/$', 'regressiontests.urlpatterns_reverse.views.missing_view'),
# View is not callable
url(r'uncallable/$', 'regressiontests.urlpatterns_reverse.views.uncallable'),
# Module does not exist
url(r'missing_outer/$', 'regressiontests.urlpatterns_reverse.missing_module.missing_view'),
)
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Unit tests for reverse URL lookups. Unit tests for reverse URL lookups.
""" """
from django.conf import settings from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
from django.core.urlresolvers import reverse, resolve, NoReverseMatch,\ from django.core.urlresolvers import reverse, resolve, NoReverseMatch,\
Resolver404, ResolverMatch,\ Resolver404, ResolverMatch,\
RegexURLResolver, RegexURLPattern RegexURLResolver, RegexURLPattern
...@@ -470,3 +470,14 @@ class ResolverMatchTests(TestCase): ...@@ -470,3 +470,14 @@ class ResolverMatchTests(TestCase):
self.assertEqual(match[0], func) self.assertEqual(match[0], func)
self.assertEqual(match[1], args) self.assertEqual(match[1], args)
self.assertEqual(match[2], kwargs) self.assertEqual(match[2], kwargs)
class ErroneousViewTests(TestCase):
urls = 'regressiontests.urlpatterns_reverse.erroneous_urls'
def test_erroneous_resolve(self):
self.assertRaises(ImportError, self.client.get, '/erroneous_inner/')
self.assertRaises(ImportError, self.client.get, '/erroneous_outer/')
self.assertRaises(ViewDoesNotExist, self.client.get, '/missing_inner/')
self.assertRaises(ViewDoesNotExist, self.client.get, '/missing_outer/')
self.assertRaises(ViewDoesNotExist, self.client.get, '/uncallable/')
...@@ -16,6 +16,11 @@ def absolute_kwargs_view(request, arg1=1, arg2=2): ...@@ -16,6 +16,11 @@ def absolute_kwargs_view(request, arg1=1, arg2=2):
def defaults_view(request, arg1, arg2): def defaults_view(request, arg1, arg2):
pass pass
def erroneous_view(request):
import non_existent
uncallable = "Can I be a view? Pleeeease?"
class ViewClass(object): class ViewClass(object):
def __call__(self, request, *args, **kwargs): def __call__(self, request, *args, **kwargs):
return HttpResponse('') return HttpResponse('')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment