summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuan Catalano <catalanojuan@gmail.com>2013-09-06 20:23:25 -0300
committerTim Graham <timograham@gmail.com>2013-09-13 20:09:41 -0400
commit6feb75129f42fdac751d0b79a2a005b4c0ad5c2d (patch)
tree5bb6cbb83482c15af59cfce69ae2339ff2aa0f3d
parent39b49fd33970720b973498d2c8275ced129d3a7a (diff)
Fixed #21060 -- Refactored admin's autodiscover method to make it reusable.
We want to be able to use it for instance for discovering `tasks.py` modules inside the INSTALLED_APPS. This commit therefore moves the logic to `autodiscover_modules` method in django.utils.module_loading.
-rw-r--r--django/contrib/admin/__init__.py31
-rw-r--r--django/utils/module_loading.py38
-rw-r--r--tests/utils_tests/test_module/__init__.py3
-rw-r--r--tests/utils_tests/test_module/another_bad_module.py8
-rw-r--r--tests/utils_tests/test_module/another_good_module.py1
-rw-r--r--tests/utils_tests/test_module_loading.py31
6 files changed, 82 insertions, 30 deletions
diff --git a/django/contrib/admin/__init__.py b/django/contrib/admin/__init__.py
index ef2e87407a..3975993679 100644
--- a/django/contrib/admin/__init__.py
+++ b/django/contrib/admin/__init__.py
@@ -7,35 +7,8 @@ from django.contrib.admin.sites import AdminSite, site
from django.contrib.admin.filters import (ListFilter, SimpleListFilter,
FieldListFilter, BooleanFieldListFilter, RelatedFieldListFilter,
ChoicesFieldListFilter, DateFieldListFilter, AllValuesFieldListFilter)
+from django.utils.module_loading import autodiscover_modules
def autodiscover():
- """
- Auto-discover INSTALLED_APPS admin.py modules and fail silently when
- not present. This forces an import on them to register any admin bits they
- may want.
- """
-
- import copy
- from importlib import import_module
- from django.conf import settings
- from django.utils.module_loading import module_has_submodule
-
- for app in settings.INSTALLED_APPS:
- mod = import_module(app)
- # Attempt to import the app's admin module.
- try:
- before_import_registry = copy.copy(site._registry)
- import_module('%s.admin' % app)
- except:
- # Reset the model registry to the state before the last import as
- # this import will have to reoccur on the next request and this
- # could raise NotRegistered and AlreadyRegistered exceptions
- # (see #8245).
- site._registry = before_import_registry
-
- # Decide whether to bubble up this error. If the app just
- # doesn't have an admin module, we can ignore the error
- # attempting to import it, otherwise we want it to bubble up.
- if module_has_submodule(mod, 'admin'):
- raise
+ autodiscover_modules('admin', register_to=site)
diff --git a/django/utils/module_loading.py b/django/utils/module_loading.py
index 9c8ea98d50..8868a2d3ab 100644
--- a/django/utils/module_loading.py
+++ b/django/utils/module_loading.py
@@ -1,5 +1,6 @@
from __future__ import absolute_import # Avoid importing `importlib` from this package.
+import copy
import imp
from importlib import import_module
import os
@@ -34,6 +35,43 @@ def import_by_path(dotted_path, error_prefix=''):
return attr
+def autodiscover_modules(*args, **kwargs):
+ """
+ Auto-discover INSTALLED_APPS modules and fail silently when
+ not present. This forces an import on them to register any admin bits they
+ may want.
+
+ You may provide a register_to keyword parameter as a way to access a
+ registry. This register_to object must have a _registry instance variable
+ to access it.
+ """
+ from django.conf import settings
+
+ register_to = kwargs.get('register_to')
+ for app in settings.INSTALLED_APPS:
+ mod = import_module(app)
+ # Attempt to import the app's module.
+ try:
+ if register_to:
+ before_import_registry = copy.copy(register_to._registry)
+
+ for module_to_search in args:
+ import_module('%s.%s' % (app, module_to_search))
+ except:
+ # Reset the model registry to the state before the last import as
+ # this import will have to reoccur on the next request and this
+ # could raise NotRegistered and AlreadyRegistered exceptions
+ # (see #8245).
+ if register_to:
+ register_to._registry = before_import_registry
+
+ # Decide whether to bubble up this error. If the app just
+ # doesn't have an admin module, we can ignore the error
+ # attempting to import it, otherwise we want it to bubble up.
+ if module_has_submodule(mod, module_to_search):
+ raise
+
+
def module_has_submodule(package, module_name):
"""See if 'module' is in 'package'."""
name = ".".join([package.__name__, module_name])
diff --git a/tests/utils_tests/test_module/__init__.py b/tests/utils_tests/test_module/__init__.py
index e69de29bb2..8f33921eb6 100644
--- a/tests/utils_tests/test_module/__init__.py
+++ b/tests/utils_tests/test_module/__init__.py
@@ -0,0 +1,3 @@
+class SiteMock(object):
+ _registry = {}
+site = SiteMock()
diff --git a/tests/utils_tests/test_module/another_bad_module.py b/tests/utils_tests/test_module/another_bad_module.py
new file mode 100644
index 0000000000..eac25c4aa5
--- /dev/null
+++ b/tests/utils_tests/test_module/another_bad_module.py
@@ -0,0 +1,8 @@
+from . import site
+content = 'Another Bad Module'
+
+site._registry.update({
+ 'foo': 'bar',
+})
+
+raise Exception('Some random exception.')
diff --git a/tests/utils_tests/test_module/another_good_module.py b/tests/utils_tests/test_module/another_good_module.py
new file mode 100644
index 0000000000..ed4b2d34a2
--- /dev/null
+++ b/tests/utils_tests/test_module/another_good_module.py
@@ -0,0 +1 @@
+content = 'Another Good Module'
diff --git a/tests/utils_tests/test_module_loading.py b/tests/utils_tests/test_module_loading.py
index 2423215778..d808ab8783 100644
--- a/tests/utils_tests/test_module_loading.py
+++ b/tests/utils_tests/test_module_loading.py
@@ -6,7 +6,10 @@ import unittest
from zipimport import zipimporter
from django.core.exceptions import ImproperlyConfigured
-from django.utils.module_loading import import_by_path, module_has_submodule
+from django.test import SimpleTestCase
+from django.test.utils import override_settings
+from django.utils import six
+from django.utils.module_loading import autodiscover_modules, import_by_path, module_has_submodule
from django.utils._os import upath
@@ -130,6 +133,32 @@ class ModuleImportTestCase(unittest.TestCase):
self.assertIsNotNone(traceback.tb_next.tb_next,
'Should have more than the calling frame in the traceback.')
+@override_settings(INSTALLED_APPS=('utils_tests.test_module',))
+class AutodiscoverModulesTestCase(SimpleTestCase):
+
+ def test_autodiscover_modules_found(self):
+ autodiscover_modules('good_module')
+
+ def test_autodiscover_modules_not_found(self):
+ autodiscover_modules('missing_module')
+
+ def test_autodiscover_modules_found_but_bad_module(self):
+ with six.assertRaisesRegex(self, ImportError, "No module named '?a_package_name_that_does_not_exist'?"):
+ autodiscover_modules('bad_module')
+
+ def test_autodiscover_modules_several_one_bad_module(self):
+ with six.assertRaisesRegex(self, ImportError, "No module named '?a_package_name_that_does_not_exist'?"):
+ autodiscover_modules('good_module', 'bad_module')
+
+ def test_autodiscover_modules_several_found(self):
+ autodiscover_modules('good_module', 'another_good_module')
+
+ def test_validate_registry_keeps_intact(self):
+ from .test_module import site
+ with six.assertRaisesRegex(self, Exception, "Some random exception."):
+ autodiscover_modules('another_bad_module', register_to=site)
+ self.assertEqual(site._registry, {})
+
class ProxyFinder(object):
def __init__(self):