summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJannis Leidel <jannis@leidel.info>2011-06-16 16:41:14 +0000
committerJannis Leidel <jannis@leidel.info>2011-06-16 16:41:14 +0000
commitb4cdf4d111e2a536abddeb38d029e71bb0d41ddb (patch)
treeebb2516334a920781f644c39a3069641760b3802
parent30e842632e6ed95828241fb62f50eb1da9e4019c (diff)
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
-rw-r--r--django/core/urlresolvers.py39
-rw-r--r--tests/regressiontests/urlpatterns_reverse/erroneous_urls.py14
-rw-r--r--tests/regressiontests/urlpatterns_reverse/erroneous_views_module.py4
-rw-r--r--tests/regressiontests/urlpatterns_reverse/tests.py13
-rw-r--r--tests/regressiontests/urlpatterns_reverse/views.py5
5 files changed, 55 insertions, 20 deletions
diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py
index 5a78c9bfc3..72f4f20e4b 100644
--- a/django/core/urlresolvers.py
+++ b/django/core/urlresolvers.py
@@ -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.functional import memoize, lazy
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.translation import get_language
@@ -84,19 +85,28 @@ def get_callable(lookup_view, can_fail=False):
during the import fail and the string is returned.
"""
if not callable(lookup_view):
+ mod_name, func_name = get_mod_func(lookup_view)
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 != '':
lookup_view = getattr(import_module(mod_name), func_name)
if not callable(lookup_view):
- raise AttributeError("'%s.%s' is not a callable." % (mod_name, func_name))
- except (ImportError, AttributeError):
+ raise ViewDoesNotExist(
+ "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:
raise
- except UnicodeEncodeError:
- pass
return lookup_view
get_callable = memoize(get_callable, _callable_cache, 1)
@@ -192,14 +202,8 @@ class RegexURLPattern(LocaleRegexProvider):
def callback(self):
if self._callback is not None:
return self._callback
- try:
- 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)))
+
+ self._callback = get_callable(self._callback_str)
return self._callback
class RegexURLResolver(LocaleRegexProvider):
@@ -325,10 +329,7 @@ class RegexURLResolver(LocaleRegexProvider):
# Lazy import, since urls.defaults imports this file
from django.conf.urls import defaults
callback = getattr(defaults, 'handler%s' % view_type)
- try:
- return get_callable(callback), {}
- except (ImportError, AttributeError), e:
- raise ViewDoesNotExist("Tried %s. Error was: %s" % (callback, str(e)))
+ return get_callable(callback), {}
def resolve404(self):
return self._resolve_special('404')
diff --git a/tests/regressiontests/urlpatterns_reverse/erroneous_urls.py b/tests/regressiontests/urlpatterns_reverse/erroneous_urls.py
new file mode 100644
index 0000000000..b09fff7e3e
--- /dev/null
+++ b/tests/regressiontests/urlpatterns_reverse/erroneous_urls.py
@@ -0,0 +1,14 @@
+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'),
+)
diff --git a/tests/regressiontests/urlpatterns_reverse/erroneous_views_module.py b/tests/regressiontests/urlpatterns_reverse/erroneous_views_module.py
new file mode 100644
index 0000000000..7f6b75e00f
--- /dev/null
+++ b/tests/regressiontests/urlpatterns_reverse/erroneous_views_module.py
@@ -0,0 +1,4 @@
+import non_existent
+
+def erroneous_view(request):
+ pass
diff --git a/tests/regressiontests/urlpatterns_reverse/tests.py b/tests/regressiontests/urlpatterns_reverse/tests.py
index 31a928caa2..f447adf4cc 100644
--- a/tests/regressiontests/urlpatterns_reverse/tests.py
+++ b/tests/regressiontests/urlpatterns_reverse/tests.py
@@ -2,7 +2,7 @@
Unit tests for reverse URL lookups.
"""
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,\
Resolver404, ResolverMatch,\
RegexURLResolver, RegexURLPattern
@@ -470,3 +470,14 @@ class ResolverMatchTests(TestCase):
self.assertEqual(match[0], func)
self.assertEqual(match[1], args)
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/')
+
diff --git a/tests/regressiontests/urlpatterns_reverse/views.py b/tests/regressiontests/urlpatterns_reverse/views.py
index 2d10aee979..f631acf3ec 100644
--- a/tests/regressiontests/urlpatterns_reverse/views.py
+++ b/tests/regressiontests/urlpatterns_reverse/views.py
@@ -16,6 +16,11 @@ def absolute_kwargs_view(request, arg1=1, arg2=2):
def defaults_view(request, arg1, arg2):
pass
+def erroneous_view(request):
+ import non_existent
+
+uncallable = "Can I be a view? Pleeeease?"
+
class ViewClass(object):
def __call__(self, request, *args, **kwargs):
return HttpResponse('')