From 367bfaa5226eaae3278989e63f16063d5cc46cd8 Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Tue, 14 Aug 2012 21:12:08 +0200 Subject: [PATCH] Don't swallow AttributeError in core.urlresolvers.get_callable. --- django/core/urlresolvers.py | 27 +++++++++++-------- .../urlpatterns_reverse/tests.py | 18 +++++++++++-- .../urlpatterns_reverse/views_broken.py | 2 ++ 3 files changed, 34 insertions(+), 13 deletions(-) create mode 100644 tests/regressiontests/urlpatterns_reverse/views_broken.py diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py index 1498c9989d..b83f2f51b0 100644 --- a/django/core/urlresolvers.py +++ b/django/core/urlresolvers.py @@ -89,18 +89,11 @@ def get_callable(lookup_view, can_fail=False): """ if not callable(lookup_view): mod_name, func_name = get_mod_func(lookup_view) + if func_name == '': + return lookup_view + try: - if func_name != '': - lookup_view = getattr(import_module(mod_name), func_name) - if not callable(lookup_view): - 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)) + mod = import_module(mod_name) except ImportError: parentmod, submod = get_mod_func(mod_name) if (not can_fail and submod != '' and @@ -110,6 +103,18 @@ def get_callable(lookup_view, can_fail=False): (lookup_view, mod_name)) if not can_fail: raise + else: + try: + lookup_view = getattr(mod, func_name) + if not callable(lookup_view): + 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)) return lookup_view get_callable = memoize(get_callable, _callable_cache, 1) diff --git a/tests/regressiontests/urlpatterns_reverse/tests.py b/tests/regressiontests/urlpatterns_reverse/tests.py index 207cc56488..b9c64183e5 100644 --- a/tests/regressiontests/urlpatterns_reverse/tests.py +++ b/tests/regressiontests/urlpatterns_reverse/tests.py @@ -5,8 +5,9 @@ from __future__ import absolute_import, unicode_literals from django.conf import settings from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist -from django.core.urlresolvers import (reverse, resolve, NoReverseMatch, - Resolver404, ResolverMatch, RegexURLResolver, RegexURLPattern) +from django.core.urlresolvers import (reverse, resolve, get_callable, + NoReverseMatch, Resolver404, ResolverMatch, RegexURLResolver, + RegexURLPattern) from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect from django.shortcuts import redirect from django.test import TestCase @@ -519,3 +520,16 @@ class ErroneousViewTests(TestCase): """ # The regex error will be hit before NoReverseMatch can be raised self.assertRaises(ImproperlyConfigured, reverse, 'whatever blah blah') + +class ViewLoadingTests(TestCase): + def test_view_loading(self): + # A missing view (identified by an AttributeError) should raise + # ViewDoesNotExist, ... + self.assertRaisesRegexp(ViewDoesNotExist, ".*View does not exist in.*", + get_callable, + 'regressiontests.urlpatterns_reverse.views.i_should_not_exist') + # ... but if the AttributeError is caused by something else don't + # swallow it. + self.assertRaises(AttributeError, get_callable, + 'regressiontests.urlpatterns_reverse.views_broken.i_am_broken') + diff --git a/tests/regressiontests/urlpatterns_reverse/views_broken.py b/tests/regressiontests/urlpatterns_reverse/views_broken.py new file mode 100644 index 0000000000..4953aab239 --- /dev/null +++ b/tests/regressiontests/urlpatterns_reverse/views_broken.py @@ -0,0 +1,2 @@ +# I just raise an AttributeError to confuse the view loading mechanism +raise AttributeError('I am here to confuse django.core.urlresolvers.get_callable')