Commit b6ad1afa authored by Carl Meyer's avatar Carl Meyer
Browse files

Fixed #17011 - Made override_settings modify a decorated class in-place rather...

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
parent 0f5d6915
Loading
Loading
Loading
Loading
+7 −13
Original line number Diff line number Diff line
@@ -196,23 +196,17 @@ class override_settings(object):
    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):
+9 −0
Original line number Diff line number Diff line
@@ -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::

+32 −0
Original line number Diff line number Diff line
@@ -35,6 +35,38 @@ class FullyDecoratedTestCase(TestCase):

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')