Fixed #23670 -- Prevented partial import state during module autodiscovery

Thanks kostko for the report.
This commit is contained in:
Markus Holtermann 2014-10-31 10:22:15 +01:00 committed by Tim Graham
parent c0c1bb9e64
commit 98da408964
3 changed files with 47 additions and 17 deletions

View File

@ -64,23 +64,23 @@ def autodiscover_modules(*args, **kwargs):
register_to = kwargs.get('register_to') register_to = kwargs.get('register_to')
for app_config in apps.get_app_configs(): for app_config in apps.get_app_configs():
for module_to_search in args:
# Attempt to import the app's module. # Attempt to import the app's module.
try: try:
if register_to: if register_to:
before_import_registry = copy.copy(register_to._registry) before_import_registry = copy.copy(register_to._registry)
for module_to_search in args:
import_module('%s.%s' % (app_config.name, module_to_search)) import_module('%s.%s' % (app_config.name, module_to_search))
except: except:
# Reset the model registry to the state before the last import as # Reset the registry to the state before the last import
# this import will have to reoccur on the next request and this # as this import will have to reoccur on the next request and
# could raise NotRegistered and AlreadyRegistered exceptions # this could raise NotRegistered and AlreadyRegistered
# (see #8245). # exceptions (see #8245).
if register_to: if register_to:
register_to._registry = before_import_registry register_to._registry = before_import_registry
# Decide whether to bubble up this error. If the app just # Decide whether to bubble up this error. If the app just
# doesn't have an admin module, we can ignore the error # doesn't have the module in question, we can ignore the error
# attempting to import it, otherwise we want it to bubble up. # attempting to import it, otherwise we want it to bubble up.
if module_has_submodule(app_config.module, module_to_search): if module_has_submodule(app_config.module, module_to_search):
raise raise

View File

@ -1 +1,6 @@
from . import site
content = 'Another Good Module' content = 'Another Good Module'
site._registry.update({
'lorem': 'ipsum',
})

View File

@ -155,6 +155,15 @@ class ModuleImportTestCase(IgnoreDeprecationWarningsMixin, unittest.TestCase):
@modify_settings(INSTALLED_APPS={'append': 'utils_tests.test_module'}) @modify_settings(INSTALLED_APPS={'append': 'utils_tests.test_module'})
class AutodiscoverModulesTestCase(SimpleTestCase): class AutodiscoverModulesTestCase(SimpleTestCase):
def tearDown(self):
sys.path_importer_cache.clear()
sys.modules.pop('utils_tests.test_module.another_bad_module', None)
sys.modules.pop('utils_tests.test_module.another_good_module', None)
sys.modules.pop('utils_tests.test_module.bad_module', None)
sys.modules.pop('utils_tests.test_module.good_module', None)
sys.modules.pop('utils_tests.test_module', None)
def test_autodiscover_modules_found(self): def test_autodiscover_modules_found(self):
autodiscover_modules('good_module') autodiscover_modules('good_module')
@ -172,12 +181,28 @@ class AutodiscoverModulesTestCase(SimpleTestCase):
def test_autodiscover_modules_several_found(self): def test_autodiscover_modules_several_found(self):
autodiscover_modules('good_module', 'another_good_module') autodiscover_modules('good_module', 'another_good_module')
def test_autodiscover_modules_several_found_with_registry(self):
from .test_module import site
autodiscover_modules('good_module', 'another_good_module', register_to=site)
self.assertEqual(site._registry, {'lorem': 'ipsum'})
def test_validate_registry_keeps_intact(self): def test_validate_registry_keeps_intact(self):
from .test_module import site from .test_module import site
with six.assertRaisesRegex(self, Exception, "Some random exception."): with six.assertRaisesRegex(self, Exception, "Some random exception."):
autodiscover_modules('another_bad_module', register_to=site) autodiscover_modules('another_bad_module', register_to=site)
self.assertEqual(site._registry, {}) self.assertEqual(site._registry, {})
def test_validate_registry_resets_after_erroneous_module(self):
from .test_module import site
with six.assertRaisesRegex(self, Exception, "Some random exception."):
autodiscover_modules('another_good_module', 'another_bad_module', register_to=site)
self.assertEqual(site._registry, {'lorem': 'ipsum'})
def test_validate_registry_resets_after_missing_module(self):
from .test_module import site
autodiscover_modules('does_not_exist', 'another_good_module', 'does_not_exist2', register_to=site)
self.assertEqual(site._registry, {'lorem': 'ipsum'})
class ProxyFinder(object): class ProxyFinder(object):
def __init__(self): def __init__(self):