Fixed override_settings when set_available_apps raises an exception.

Previously, this would corrupt the settings, because __exit__ isn't
called when __enter__raises an exception.
This commit is contained in:
Aymeric Augustin 2013-12-24 00:07:54 +01:00
parent 7577d03809
commit 2ec8e3443b
2 changed files with 24 additions and 15 deletions

View File

@ -52,7 +52,7 @@ class AppCache(object):
# Cache for get_models. # Cache for get_models.
self._get_models_cache = {} self._get_models_cache = {}
def populate_apps(self): def populate_apps(self, installed_apps=None):
""" """
Populate app-related information. Populate app-related information.
@ -77,7 +77,9 @@ class AppCache(object):
# Application modules aren't expected to import anything, and # Application modules aren't expected to import anything, and
# especially not other application modules, even indirectly. # especially not other application modules, even indirectly.
# Therefore we simply import them sequentially. # Therefore we simply import them sequentially.
for app_name in settings.INSTALLED_APPS: if installed_apps is None:
installed_apps = settings.INSTALLED_APPS
for app_name in installed_apps:
app_config = AppConfig.create(app_name) app_config = AppConfig.create(app_name)
self.app_configs[app_config.label] = app_config self.app_configs[app_config.label] = app_config
@ -299,6 +301,8 @@ class AppCache(object):
available must be an iterable of application names. available must be an iterable of application names.
set_available_apps() must be balanced with unset_available_apps().
Primarily used for performance optimization in TransactionTestCase. Primarily used for performance optimization in TransactionTestCase.
This method is safe is the sense that it doesn't trigger any imports. This method is safe is the sense that it doesn't trigger any imports.
@ -323,10 +327,13 @@ class AppCache(object):
def set_installed_apps(self, installed): def set_installed_apps(self, installed):
""" """
Enables a different set of installed_apps for get_app_config[s]. Enables a different set of installed apps for get_app_config[s].
installed must be an iterable in the same format as INSTALLED_APPS. installed must be an iterable in the same format as INSTALLED_APPS.
set_installed_apps() must be balanced with unset_installed_apps(),
even if it exits with an exception.
Primarily used as a receiver of the setting_changed signal in tests. Primarily used as a receiver of the setting_changed signal in tests.
This method may trigger new imports, which may add new models to the This method may trigger new imports, which may add new models to the
@ -337,14 +344,10 @@ class AppCache(object):
""" """
self.stored_app_configs.append(self.app_configs) self.stored_app_configs.append(self.app_configs)
self.app_configs = OrderedDict() self.app_configs = OrderedDict()
try:
self._apps_loaded = False self._apps_loaded = False
self.populate_apps() self.populate_apps(installed)
self._models_loaded = False self._models_loaded = False
self.populate_models() self.populate_models()
except Exception:
self.unset_installed_apps()
raise
def unset_installed_apps(self): def unset_installed_apps(self):
""" """

View File

@ -225,22 +225,28 @@ class override_settings(object):
test_func._overridden_settings, **self.options) test_func._overridden_settings, **self.options)
def enable(self): def enable(self):
# Keep this code at the beginning to leave the settings unchanged
# in case it raises an exception because INSTALLED_APPS is invalid.
if 'INSTALLED_APPS' in self.options:
try:
app_cache.set_installed_apps(self.options['INSTALLED_APPS'])
except Exception:
app_cache.unset_installed_apps()
raise
override = UserSettingsHolder(settings._wrapped) override = UserSettingsHolder(settings._wrapped)
for key, new_value in self.options.items(): for key, new_value in self.options.items():
setattr(override, key, new_value) setattr(override, key, new_value)
self.wrapped = settings._wrapped self.wrapped = settings._wrapped
settings._wrapped = override settings._wrapped = override
if 'INSTALLED_APPS' in self.options:
app_cache.set_installed_apps(settings.INSTALLED_APPS)
for key, new_value in self.options.items(): for key, new_value in self.options.items():
setting_changed.send(sender=settings._wrapped.__class__, setting_changed.send(sender=settings._wrapped.__class__,
setting=key, value=new_value, enter=True) setting=key, value=new_value, enter=True)
def disable(self): def disable(self):
settings._wrapped = self.wrapped
del self.wrapped
if 'INSTALLED_APPS' in self.options: if 'INSTALLED_APPS' in self.options:
app_cache.unset_installed_apps() app_cache.unset_installed_apps()
settings._wrapped = self.wrapped
del self.wrapped
for key in self.options: for key in self.options:
new_value = getattr(settings, key, None) new_value = getattr(settings, key, None)
setting_changed.send(sender=settings._wrapped.__class__, setting_changed.send(sender=settings._wrapped.__class__,