Fixed #17011 - Made override_settings modify a decorated class in-place rather than creating a dynamic subclass, so as to avoid infinite recursion when used with super(). Thanks jsdalton for the report and patch.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@16942 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
0f5d69155e
commit
b6ad1afa68
|
@ -196,23 +196,17 @@ class override_settings(object):
|
||||||
def __call__(self, test_func):
|
def __call__(self, test_func):
|
||||||
from django.test import TransactionTestCase
|
from django.test import TransactionTestCase
|
||||||
if isinstance(test_func, type) and issubclass(test_func, TransactionTestCase):
|
if isinstance(test_func, type) and issubclass(test_func, TransactionTestCase):
|
||||||
# When decorating a class, we need to construct a new class
|
original_pre_setup = test_func._pre_setup
|
||||||
# with the same name so that the test discovery tools can
|
original_post_teardown = test_func._post_teardown
|
||||||
# get a useful name.
|
|
||||||
def _pre_setup(innerself):
|
def _pre_setup(innerself):
|
||||||
self.enable()
|
self.enable()
|
||||||
test_func._pre_setup(innerself)
|
original_pre_setup(innerself)
|
||||||
def _post_teardown(innerself):
|
def _post_teardown(innerself):
|
||||||
test_func._post_teardown(innerself)
|
original_post_teardown(innerself)
|
||||||
self.disable()
|
self.disable()
|
||||||
inner = type(
|
test_func._pre_setup = _pre_setup
|
||||||
test_func.__name__,
|
test_func._post_teardown = _post_teardown
|
||||||
(test_func,),
|
return test_func
|
||||||
{
|
|
||||||
'_pre_setup': _pre_setup,
|
|
||||||
'_post_teardown': _post_teardown,
|
|
||||||
'__module__': test_func.__module__,
|
|
||||||
})
|
|
||||||
else:
|
else:
|
||||||
@wraps(test_func)
|
@wraps(test_func)
|
||||||
def inner(*args, **kwargs):
|
def inner(*args, **kwargs):
|
||||||
|
|
|
@ -1450,6 +1450,15 @@ The decorator can also be applied to test case classes::
|
||||||
|
|
||||||
LoginTestCase = override_settings(LOGIN_URL='/other/login/')(LoginTestCase)
|
LoginTestCase = override_settings(LOGIN_URL='/other/login/')(LoginTestCase)
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
When given a class, the decorator modifies the class directly and
|
||||||
|
returns it; it doesn't create and return a modified copy of it. So if
|
||||||
|
you try to tweak the above example to assign the return value to a
|
||||||
|
different name than ``LoginTestCase``, you may be surprised to find that
|
||||||
|
the original ``LoginTestCase`` is still equally affected by the
|
||||||
|
decorator.
|
||||||
|
|
||||||
On Python 2.6 and higher you can also use the well known decorator syntax to
|
On Python 2.6 and higher you can also use the well known decorator syntax to
|
||||||
decorate the class::
|
decorate the class::
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,38 @@ class FullyDecoratedTestCase(TestCase):
|
||||||
|
|
||||||
FullyDecoratedTestCase = override_settings(TEST='override')(FullyDecoratedTestCase)
|
FullyDecoratedTestCase = override_settings(TEST='override')(FullyDecoratedTestCase)
|
||||||
|
|
||||||
|
|
||||||
|
class ClassDecoratedTestCaseSuper(TestCase):
|
||||||
|
"""
|
||||||
|
Dummy class for testing max recursion error in child class call to
|
||||||
|
super(). Refs #17011.
|
||||||
|
|
||||||
|
"""
|
||||||
|
def test_max_recursion_error(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(TEST='override')
|
||||||
|
class ClassDecoratedTestCase(ClassDecoratedTestCaseSuper):
|
||||||
|
def test_override(self):
|
||||||
|
self.assertEqual(settings.TEST, 'override')
|
||||||
|
|
||||||
|
@override_settings(TEST='override2')
|
||||||
|
def test_method_override(self):
|
||||||
|
self.assertEqual(settings.TEST, 'override2')
|
||||||
|
|
||||||
|
def test_max_recursion_error(self):
|
||||||
|
"""
|
||||||
|
Overriding a method on a super class and then calling that method on
|
||||||
|
the super class should not trigger infinite recursion. See #17011.
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
super(ClassDecoratedTestCase, self).test_max_recursion_error()
|
||||||
|
except RuntimeError, e:
|
||||||
|
self.fail()
|
||||||
|
|
||||||
|
|
||||||
class SettingGetter(object):
|
class SettingGetter(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.test = getattr(settings, 'TEST', 'undefined')
|
self.test = getattr(settings, 'TEST', 'undefined')
|
||||||
|
|
Loading…
Reference in New Issue