Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #10802 -- Handle ImportErrors and AttributeErrors gracefully wh…

…en 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
  • Loading branch information...
commit b4cdf4d111e2a536abddeb38d029e71bb0d41ddb 1 parent 30e8426
Jannis Leidel authored June 16, 2011
39  django/core/urlresolvers.py
@@ -16,6 +16,7 @@
16 16
 from django.utils.encoding import iri_to_uri, force_unicode, smart_str
17 17
 from django.utils.functional import memoize, lazy
18 18
 from django.utils.importlib import import_module
  19
+from django.utils.module_loading import module_has_submodule
19 20
 from django.utils.regex_helper import normalize
20 21
 from django.utils.translation import get_language
21 22
 
@@ -84,19 +85,28 @@ def get_callable(lookup_view, can_fail=False):
84 85
     during the import fail and the string is returned.
85 86
     """
86 87
     if not callable(lookup_view):
  88
+        mod_name, func_name = get_mod_func(lookup_view)
87 89
         try:
88  
-            # Bail early for non-ASCII strings (they can't be functions).
89  
-            lookup_view = lookup_view.encode('ascii')
90  
-            mod_name, func_name = get_mod_func(lookup_view)
91 90
             if func_name != '':
92 91
                 lookup_view = getattr(import_module(mod_name), func_name)
93 92
                 if not callable(lookup_view):
94  
-                    raise AttributeError("'%s.%s' is not a callable." % (mod_name, func_name))
95  
-        except (ImportError, AttributeError):
  93
+                    raise ViewDoesNotExist(
  94
+                        "Could not import %s.%s. View is not callable."
  95
+                    % (mod_name, func_name))
  96
+        except AttributeError:
  97
+            if not can_fail:
  98
+                raise ViewDoesNotExist(
  99
+                    "Could not import %s. View does not exist in module %s."
  100
+                    % (lookup_view, mod_name))
  101
+        except ImportError:
  102
+            ownermod, submod = get_mod_func(mod_name)
  103
+            if (not can_fail and submod != '' and
  104
+                    not module_has_submodule(import_module(ownermod), submod)):
  105
+                raise ViewDoesNotExist(
  106
+                    "Could not import %s. Owning module %s does not exist."
  107
+                    % (lookup_view, mod_name))
96 108
             if not can_fail:
97 109
                 raise
98  
-        except UnicodeEncodeError:
99  
-            pass
100 110
     return lookup_view
101 111
 get_callable = memoize(get_callable, _callable_cache, 1)
102 112
 
@@ -192,14 +202,8 @@ def resolve(self, path):
192 202
     def callback(self):
193 203
         if self._callback is not None:
194 204
             return self._callback
195  
-        try:
196  
-            self._callback = get_callable(self._callback_str)
197  
-        except ImportError, e:
198  
-            mod_name, _ = get_mod_func(self._callback_str)
199  
-            raise ViewDoesNotExist("Could not import %s. Error was: %s" % (mod_name, str(e)))
200  
-        except AttributeError, e:
201  
-            mod_name, func_name = get_mod_func(self._callback_str)
202  
-            raise ViewDoesNotExist("Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e)))
  205
+
  206
+        self._callback = get_callable(self._callback_str)
203 207
         return self._callback
204 208
 
205 209
 class RegexURLResolver(LocaleRegexProvider):
@@ -325,10 +329,7 @@ def _resolve_special(self, view_type):
325 329
             # Lazy import, since urls.defaults imports this file
326 330
             from django.conf.urls import defaults
327 331
             callback = getattr(defaults, 'handler%s' % view_type)
328  
-        try:
329  
-            return get_callable(callback), {}
330  
-        except (ImportError, AttributeError), e:
331  
-            raise ViewDoesNotExist("Tried %s. Error was: %s" % (callback, str(e)))
  332
+        return get_callable(callback), {}
332 333
 
333 334
     def resolve404(self):
334 335
         return self._resolve_special('404')
14  tests/regressiontests/urlpatterns_reverse/erroneous_urls.py
... ...
@@ -0,0 +1,14 @@
  1
+from django.conf.urls.defaults import patterns, url
  2
+
  3
+urlpatterns = patterns('',
  4
+    # View has erroneous import
  5
+    url(r'erroneous_inner/$', 'regressiontests.urlpatterns_reverse.views.erroneous_view'),
  6
+    # Module has erroneous import
  7
+    url(r'erroneous_outer/$', 'regressiontests.urlpatterns_reverse.erroneous_views_module.erroneous_view'),
  8
+    # View does not exist
  9
+    url(r'missing_inner/$', 'regressiontests.urlpatterns_reverse.views.missing_view'),
  10
+    # View is not callable
  11
+    url(r'uncallable/$', 'regressiontests.urlpatterns_reverse.views.uncallable'),
  12
+    # Module does not exist
  13
+    url(r'missing_outer/$', 'regressiontests.urlpatterns_reverse.missing_module.missing_view'),
  14
+)
4  tests/regressiontests/urlpatterns_reverse/erroneous_views_module.py
... ...
@@ -0,0 +1,4 @@
  1
+import non_existent
  2
+
  3
+def erroneous_view(request):
  4
+    pass
13  tests/regressiontests/urlpatterns_reverse/tests.py
@@ -2,7 +2,7 @@
2 2
 Unit tests for reverse URL lookups.
3 3
 """
4 4
 from django.conf import settings
5  
-from django.core.exceptions import ImproperlyConfigured
  5
+from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
6 6
 from django.core.urlresolvers import reverse, resolve, NoReverseMatch,\
7 7
                                      Resolver404, ResolverMatch,\
8 8
                                      RegexURLResolver, RegexURLPattern
@@ -470,3 +470,14 @@ def test_urlpattern_resolve(self):
470 470
             self.assertEqual(match[0], func)
471 471
             self.assertEqual(match[1], args)
472 472
             self.assertEqual(match[2], kwargs)
  473
+
  474
+class ErroneousViewTests(TestCase):
  475
+    urls = 'regressiontests.urlpatterns_reverse.erroneous_urls'
  476
+
  477
+    def test_erroneous_resolve(self):
  478
+        self.assertRaises(ImportError, self.client.get, '/erroneous_inner/')
  479
+        self.assertRaises(ImportError, self.client.get, '/erroneous_outer/')
  480
+        self.assertRaises(ViewDoesNotExist, self.client.get, '/missing_inner/')
  481
+        self.assertRaises(ViewDoesNotExist, self.client.get, '/missing_outer/')
  482
+        self.assertRaises(ViewDoesNotExist, self.client.get, '/uncallable/')
  483
+
5  tests/regressiontests/urlpatterns_reverse/views.py
@@ -16,6 +16,11 @@ def absolute_kwargs_view(request, arg1=1, arg2=2):
16 16
 def defaults_view(request, arg1, arg2):
17 17
     pass
18 18
 
  19
+def erroneous_view(request):
  20
+    import non_existent
  21
+
  22
+uncallable = "Can I be a view? Pleeeease?"
  23
+
19 24
 class ViewClass(object):
20 25
     def __call__(self, request, *args, **kwargs):
21 26
         return HttpResponse('')

0 notes on commit b4cdf4d

Please sign in to comment.
Something went wrong with that request. Please try again.