Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #17011 - Made override_settings modify a decorated class in-pla…

…ce 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
  • Loading branch information...
commit b6ad1afa6813226ff7d16f9f7b5751cb2d624c4b 1 parent 0f5d691
@carljm carljm authored
View
20 django/test/utils.py
@@ -196,23 +196,17 @@ def __exit__(self, exc_type, exc_value, traceback):
def __call__(self, test_func):
from django.test import TransactionTestCase
if isinstance(test_func, type) and issubclass(test_func, TransactionTestCase):
- # When decorating a class, we need to construct a new class
- # with the same name so that the test discovery tools can
- # get a useful name.
+ original_pre_setup = test_func._pre_setup
+ original_post_teardown = test_func._post_teardown
def _pre_setup(innerself):
self.enable()
- test_func._pre_setup(innerself)
+ original_pre_setup(innerself)
def _post_teardown(innerself):
- test_func._post_teardown(innerself)
+ original_post_teardown(innerself)
self.disable()
- inner = type(
- test_func.__name__,
- (test_func,),
- {
- '_pre_setup': _pre_setup,
- '_post_teardown': _post_teardown,
- '__module__': test_func.__module__,
- })
+ test_func._pre_setup = _pre_setup
+ test_func._post_teardown = _post_teardown
+ return test_func
else:
@wraps(test_func)
def inner(*args, **kwargs):
View
9 docs/topics/testing.txt
@@ -1450,6 +1450,15 @@ The decorator can also be applied to test case classes::
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
decorate the class::
View
32 tests/regressiontests/settings_tests/tests.py
@@ -35,6 +35,38 @@ def test_method_override(self):
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):
def __init__(self):
self.test = getattr(settings, 'TEST', 'undefined')
Please sign in to comment.
Something went wrong with that request. Please try again.