1
0
mirror of https://github.com/django/django.git synced 2025-10-24 06:06:09 +00:00

Added modify_settings to alter settings containing lists of values.

This commit is contained in:
Aymeric Augustin
2013-12-23 12:39:19 +01:00
parent 5891990b6e
commit 5241763c81
6 changed files with 229 additions and 49 deletions

View File

@@ -8,10 +8,10 @@ from django.test.testcases import (
SimpleTestCase, LiveServerTestCase, skipIfDBFeature,
skipUnlessDBFeature
)
from django.test.utils import override_settings
from django.test.utils import modify_settings, override_settings
__all__ = [
'Client', 'RequestFactory', 'TestCase', 'TransactionTestCase',
'SimpleTestCase', 'LiveServerTestCase', 'skipIfDBFeature',
'skipUnlessDBFeature', 'override_settings',
'skipUnlessDBFeature', 'modify_settings', 'override_settings',
]

View File

@@ -32,7 +32,7 @@ from django.test.client import Client
from django.test.html import HTMLParseError, parse_html
from django.test.signals import setting_changed, template_rendered
from django.test.utils import (CaptureQueriesContext, ContextList,
override_settings, compare_xml)
override_settings, modify_settings, compare_xml)
from django.utils.encoding import force_text
from django.utils import six
from django.utils.six.moves.urllib.parse import urlsplit, urlunsplit, urlparse, unquote
@@ -164,7 +164,8 @@ class SimpleTestCase(unittest.TestCase):
# The class we'll use for the test client self.client.
# Can be overridden in derived classes.
client_class = Client
_custom_settings = None
_overridden_settings = None
_modified_settings = None
def __call__(self, result=None):
"""
@@ -197,9 +198,12 @@ class SimpleTestCase(unittest.TestCase):
* If the class has a 'urls' attribute, replace ROOT_URLCONF with it.
* Clearing the mail test outbox.
"""
if self._custom_settings:
self._overridden = override_settings(**self._custom_settings)
self._overridden.enable()
if self._overridden_settings:
self._overridden_context = override_settings(**self._overridden_settings)
self._overridden_context.enable()
if self._modified_settings:
self._modified_context = modify_settings(self._modified_settings)
self._modified_context.enable()
self.client = self.client_class()
self._urlconf_setup()
mail.outbox = []
@@ -217,8 +221,10 @@ class SimpleTestCase(unittest.TestCase):
* Putting back the original ROOT_URLCONF if it was changed.
"""
self._urlconf_teardown()
if self._custom_settings:
self._overridden.disable()
if self._modified_settings:
self._modified_context.disable()
if self._overridden_settings:
self._overridden_context.disable()
def _urlconf_teardown(self):
set_urlconf(None)
@@ -233,6 +239,13 @@ class SimpleTestCase(unittest.TestCase):
"""
return override_settings(**kwargs)
def modify_settings(self, **kwargs):
"""
A context manager that temporarily applies changes a list setting and
reverts back to the original value when exiting the context.
"""
return modify_settings(**kwargs)
def assertRedirects(self, response, expected_url, status_code=302,
target_status_code=200, host=None, msg_prefix='',
fetch_redirect_response=True):

View File

@@ -24,8 +24,10 @@ from django.utils.translation import deactivate
__all__ = (
'Approximate', 'ContextList', 'get_runner', 'override_settings',
'requires_tz_support', 'setup_test_environment', 'teardown_test_environment',
'Approximate', 'ContextList', 'get_runner',
'modify_settings', 'override_settings',
'requires_tz_support',
'setup_test_environment', 'teardown_test_environment',
)
RESTORE_LOADERS_ATTR = '_original_template_source_loaders'
@@ -191,8 +193,6 @@ class override_settings(object):
"""
def __init__(self, **kwargs):
self.options = kwargs
# Special case that requires updating the app cache, a core feature.
self.installed_apps = self.options.get('INSTALLED_APPS')
def __enter__(self):
self.enable()
@@ -207,11 +207,7 @@ class override_settings(object):
raise Exception(
"Only subclasses of Django SimpleTestCase can be decorated "
"with override_settings")
if test_func._custom_settings:
test_func._custom_settings = dict(
test_func._custom_settings, **self.options)
else:
test_func._custom_settings = self.options
self.save_options(test_func)
return test_func
else:
@wraps(test_func)
@@ -220,14 +216,22 @@ class override_settings(object):
return test_func(*args, **kwargs)
return inner
def save_options(self, test_func):
if test_func._overridden_settings is None:
test_func._overridden_settings = self.options
else:
# Duplicate dict to prevent subclasses from altering their parent.
test_func._overridden_settings = dict(
test_func._overridden_settings, **self.options)
def enable(self):
override = UserSettingsHolder(settings._wrapped)
for key, new_value in self.options.items():
setattr(override, key, new_value)
self.wrapped = settings._wrapped
settings._wrapped = override
if self.installed_apps is not None:
app_cache.set_installed_apps(self.installed_apps)
if 'INSTALLED_APPS' in self.options:
app_cache.set_installed_apps(settings.INSTALLED_APPS)
for key, new_value in self.options.items():
setting_changed.send(sender=settings._wrapped.__class__,
setting=key, value=new_value, enter=True)
@@ -235,7 +239,7 @@ class override_settings(object):
def disable(self):
settings._wrapped = self.wrapped
del self.wrapped
if self.installed_apps is not None:
if 'INSTALLED_APPS' in self.options:
app_cache.unset_installed_apps()
for key in self.options:
new_value = getattr(settings, key, None)
@@ -243,6 +247,53 @@ class override_settings(object):
setting=key, value=new_value, enter=False)
class modify_settings(override_settings):
"""
Like override_settings, but makes it possible to append, prepend or remove
items instead of redefining the entire list.
"""
def __init__(self, *args, **kwargs):
if args:
# Hack used when instaciating from SimpleTestCase._pre_setup.
assert not kwargs
self.operations = args[0]
else:
assert not args
self.operations = list(kwargs.items())
def save_options(self, test_func):
if test_func._modified_settings is None:
test_func._modified_settings = self.operations
else:
# Duplicate list to prevent subclasses from altering their parent.
test_func._modified_settings = list(
test_func._modified_settings) + self.operations
def enable(self):
self.options = {}
for name, operations in self.operations:
try:
# When called from SimpleTestCase._pre_setup, values may be
# overridden several times; cumulate changes.
value = self.options[name]
except KeyError:
value = list(getattr(settings, name, []))
for action, items in operations.items():
# items my be a single value or an iterable.
if isinstance(items, six.string_types):
items = [items]
if action == 'append':
value = value + [item for item in items if item not in value]
elif action == 'prepend':
value = [item for item in items if item not in value] + value
elif action == 'remove':
value = [item for item in value if item not in items]
else:
raise ValueError("Unsupported action: %s" % action)
self.options[name] = value
super(modify_settings, self).enable()
def compare_xml(want, got):
"""Tries to do a 'xml-comparison' of want and got. Plain string
comparison doesn't always work because, for example, attribute