Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #17061 -- Factored out importing object from a dotted path

Thanks Carl Meyer for the report.
  • Loading branch information...
commit 7c5b244826be636429791a8ca76b2adc678e82e7 1 parent 3f1c7b7
Claude Paroz claudep authored
Showing with 117 additions and 256 deletions.
  1. +2 −3 django/conf/__init__.py
  2. +2 −6 django/contrib/admin/tests.py
  3. +3 −15 django/contrib/auth/__init__.py
  4. +2 −7 django/contrib/auth/hashers.py
  5. +4 −8 django/contrib/formtools/tests/wizard/loadstorage.py
  6. +6 −14 django/contrib/formtools/wizard/storage/__init__.py
  7. +1 −4 django/contrib/formtools/wizard/storage/exceptions.py
  8. +1 −24 django/contrib/messages/storage/__init__.py
  9. +2 −12 django/contrib/staticfiles/finders.py
  10. +8 −9 django/core/cache/__init__.py
  11. +2 −4 django/core/cache/backends/base.py
  12. +3 −17 django/core/files/storage.py
  13. +2 −16 django/core/files/uploadhandler.py
  14. +5 −15 django/core/handlers/base.py
  15. +2 −14 django/core/mail/__init__.py
  16. +6 −19 django/core/servers/basehttp.py
  17. +2 −14 django/core/signing.py
  18. +2 −11 django/db/utils.py
  19. +2 −12 django/template/context.py
  20. +2 −10 django/template/loader.py
  21. +26 −0 django/utils/module_loading.py
  22. +3 −12 django/views/debug.py
  23. +6 −6 tests/regressiontests/file_storage/tests.py
  24. +19 −1 tests/regressiontests/utils/module_loading.py
  25. +2 −1  tests/regressiontests/utils/tests.py
  26. +2 −2 tests/regressiontests/wsgi/tests.py
5 django/conf/__init__.py
View
@@ -15,6 +15,7 @@
from django.core.exceptions import ImproperlyConfigured
from django.utils.functional import LazyObject, empty
from django.utils import importlib
+from django.utils.module_loading import import_by_path
from django.utils import six
ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
@@ -68,9 +69,7 @@ def _configure_logging(self):
if self.LOGGING_CONFIG:
from django.utils.log import DEFAULT_LOGGING
# First find the logging configuration function ...
- logging_config_path, logging_config_func_name = self.LOGGING_CONFIG.rsplit('.', 1)
- logging_config_module = importlib.import_module(logging_config_path)
- logging_config_func = getattr(logging_config_module, logging_config_func_name)
+ logging_config_func = import_by_path(self.LOGGING_CONFIG)
logging_config_func(DEFAULT_LOGGING)
8 django/contrib/admin/tests.py
View
@@ -1,5 +1,5 @@
from django.test import LiveServerTestCase
-from django.utils.importlib import import_module
+from django.utils.module_loading import import_by_path
from django.utils.unittest import SkipTest
from django.utils.translation import ugettext as _
@@ -9,11 +9,7 @@ class AdminSeleniumWebDriverTestCase(LiveServerTestCase):
@classmethod
def setUpClass(cls):
try:
- # Import and start the WebDriver class.
- module, attr = cls.webdriver_class.rsplit('.', 1)
- mod = import_module(module)
- WebDriver = getattr(mod, attr)
- cls.selenium = WebDriver()
+ cls.selenium = import_by_path(cls.webdriver_class)()
except Exception as e:
raise SkipTest('Selenium webdriver "%s" not installed or not '
'operational: %s' % (cls.webdriver_class, str(e)))
18 django/contrib/auth/__init__.py
View
@@ -1,8 +1,8 @@
import re
-from django.core.exceptions import ImproperlyConfigured, PermissionDenied
-from django.utils.importlib import import_module
from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed
+from django.core.exceptions import ImproperlyConfigured, PermissionDenied
+from django.utils.module_loading import import_by_path
SESSION_KEY = '_auth_user_id'
BACKEND_SESSION_KEY = '_auth_user_backend'
@@ -10,19 +10,7 @@
def load_backend(path):
- i = path.rfind('.')
- module, attr = path[:i], path[i + 1:]
- try:
- mod = import_module(module)
- except ImportError as e:
- raise ImproperlyConfigured('Error importing authentication backend %s: "%s"' % (path, e))
- except ValueError:
- raise ImproperlyConfigured('Error importing authentication backends. Is AUTHENTICATION_BACKENDS a correctly defined list or tuple?')
- try:
- cls = getattr(mod, attr)
- except AttributeError:
- raise ImproperlyConfigured('Module "%s" does not define a "%s" authentication backend' % (module, attr))
- return cls()
+ return import_by_path(path)()
def get_backends():
9 django/contrib/auth/hashers.py
View
@@ -12,6 +12,7 @@
from django.core.exceptions import ImproperlyConfigured
from django.utils.crypto import (
pbkdf2, constant_time_compare, get_random_string)
+from django.utils.module_loading import import_by_path
from django.utils.translation import ugettext_noop as _
@@ -84,13 +85,7 @@ def load_hashers(password_hashers=None):
if not password_hashers:
password_hashers = settings.PASSWORD_HASHERS
for backend in password_hashers:
- try:
- mod_path, cls_name = backend.rsplit('.', 1)
- mod = importlib.import_module(mod_path)
- hasher_cls = getattr(mod, cls_name)
- except (AttributeError, ImportError, ValueError):
- raise ImproperlyConfigured("hasher not found: %s" % backend)
- hasher = hasher_cls()
+ hasher = import_by_path(backend)()
if not getattr(hasher, 'algorithm'):
raise ImproperlyConfigured("hasher doesn't specify an "
"algorithm name: %s" % backend)
12 django/contrib/formtools/tests/wizard/loadstorage.py
View
@@ -1,8 +1,6 @@
from django.test import TestCase
-from django.contrib.formtools.wizard.storage import (get_storage,
- MissingStorageModule,
- MissingStorageClass)
+from django.contrib.formtools.wizard.storage import get_storage, MissingStorage
from django.contrib.formtools.wizard.storage.base import BaseStorage
@@ -12,11 +10,9 @@ def test_load_storage(self):
type(get_storage('django.contrib.formtools.wizard.storage.base.BaseStorage', 'wizard1')),
BaseStorage)
- def test_missing_module(self):
- self.assertRaises(MissingStorageModule, get_storage,
+ def test_missing_storage(self):
+ self.assertRaises(MissingStorage, get_storage,
'django.contrib.formtools.wizard.storage.idontexist.IDontExistStorage', 'wizard1')
-
- def test_missing_class(self):
- self.assertRaises(MissingStorageClass, get_storage,
+ self.assertRaises(MissingStorage, get_storage,
'django.contrib.formtools.wizard.storage.base.IDontExistStorage', 'wizard1')
20 django/contrib/formtools/wizard/storage/__init__.py
View
@@ -1,22 +1,14 @@
-from django.utils.importlib import import_module
+from django.core.exceptions import ImproperlyConfigured
+from django.utils.module_loading import import_by_path
from django.contrib.formtools.wizard.storage.base import BaseStorage
from django.contrib.formtools.wizard.storage.exceptions import (
- MissingStorageModule, MissingStorageClass, NoFileStorageConfigured)
+ MissingStorage, NoFileStorageConfigured)
def get_storage(path, *args, **kwargs):
- i = path.rfind('.')
- module, attr = path[:i], path[i+1:]
try:
- mod = import_module(module)
- except ImportError as e:
- raise MissingStorageModule(
- 'Error loading storage %s: "%s"' % (module, e))
- try:
- storage_class = getattr(mod, attr)
- except AttributeError:
- raise MissingStorageClass(
- 'Module "%s" does not define a storage named "%s"' % (module, attr))
+ storage_class = import_by_path(path)
+ except ImproperlyConfigured as e:
+ raise MissingStorage('Error loading storage: %s' % e)
return storage_class(*args, **kwargs)
-
5 django/contrib/formtools/wizard/storage/exceptions.py
View
@@ -1,9 +1,6 @@
from django.core.exceptions import ImproperlyConfigured
-class MissingStorageModule(ImproperlyConfigured):
- pass
-
-class MissingStorageClass(ImproperlyConfigured):
+class MissingStorage(ImproperlyConfigured):
pass
class NoFileStorageConfigured(ImproperlyConfigured):
25 django/contrib/messages/storage/__init__.py
View
@@ -1,28 +1,5 @@
from django.conf import settings
-from django.core.exceptions import ImproperlyConfigured
-from django.utils.importlib import import_module
-
-
-def get_storage(import_path):
- """
- Imports the message storage class described by import_path, where
- import_path is the full Python path to the class.
- """
- try:
- dot = import_path.rindex('.')
- except ValueError:
- raise ImproperlyConfigured("%s isn't a Python path." % import_path)
- module, classname = import_path[:dot], import_path[dot + 1:]
- try:
- mod = import_module(module)
- except ImportError as e:
- raise ImproperlyConfigured('Error importing module %s: "%s"' %
- (module, e))
- try:
- return getattr(mod, classname)
- except AttributeError:
- raise ImproperlyConfigured('Module "%s" does not define a "%s" '
- 'class.' % (module, classname))
+from django.utils.module_loading import import_by_path as get_storage
# Callable with the same interface as the storage classes i.e. accepts a
14 django/contrib/staticfiles/finders.py
View
@@ -4,7 +4,7 @@
from django.core.files.storage import default_storage, Storage, FileSystemStorage
from django.utils.datastructures import SortedDict
from django.utils.functional import empty, memoize, LazyObject
-from django.utils.importlib import import_module
+from django.utils.module_loading import import_by_path
from django.utils._os import safe_join
from django.utils import six
@@ -258,17 +258,7 @@ def _get_finder(import_path):
Imports the staticfiles finder class described by import_path, where
import_path is the full Python path to the class.
"""
- module, attr = import_path.rsplit('.', 1)
- try:
- mod = import_module(module)
- except ImportError as e:
- raise ImproperlyConfigured('Error importing module %s: "%s"' %
- (module, e))
- try:
- Finder = getattr(mod, attr)
- except AttributeError:
- raise ImproperlyConfigured('Module "%s" does not define a "%s" '
- 'class.' % (module, attr))
+ Finder = import_by_path(import_path)
if not issubclass(Finder, BaseFinder):
raise ImproperlyConfigured('Finder "%s" is not a subclass of "%s"' %
(Finder, BaseFinder))
17 django/core/cache/__init__.py
View
@@ -25,6 +25,8 @@
InvalidCacheBackendError, CacheKeyWarning, BaseCache)
from django.core.exceptions import ImproperlyConfigured
from django.utils import importlib
+from django.utils.module_loading import import_by_path
+
__all__ = [
'get_cache', 'cache', 'DEFAULT_CACHE_ALIAS'
@@ -86,11 +88,10 @@ def parse_backend_conf(backend, **kwargs):
else:
try:
# Trying to import the given backend, in case it's a dotted path
- mod_path, cls_name = backend.rsplit('.', 1)
- mod = importlib.import_module(mod_path)
- backend_cls = getattr(mod, cls_name)
- except (AttributeError, ImportError, ValueError):
- raise InvalidCacheBackendError("Could not find backend '%s'" % backend)
+ backend_cls = import_by_path(backend)
+ except ImproperlyConfigured as e:
+ raise InvalidCacheBackendError("Could not find backend '%s': %s" % (
+ backend, e))
location = kwargs.pop('LOCATION', '')
return backend, location, kwargs
@@ -126,10 +127,8 @@ def get_cache(backend, **kwargs):
backend_cls = mod.CacheClass
else:
backend, location, params = parse_backend_conf(backend, **kwargs)
- mod_path, cls_name = backend.rsplit('.', 1)
- mod = importlib.import_module(mod_path)
- backend_cls = getattr(mod, cls_name)
- except (AttributeError, ImportError) as e:
+ backend_cls = import_by_path(backend)
+ except (AttributeError, ImportError, ImproperlyConfigured) as e:
raise InvalidCacheBackendError(
"Could not find backend '%s': %s" % (backend, e))
cache = backend_cls(location, params)
6 django/core/cache/backends/base.py
View
@@ -4,7 +4,7 @@
import warnings
from django.core.exceptions import ImproperlyConfigured, DjangoRuntimeWarning
-from django.utils.importlib import import_module
+from django.utils.module_loading import import_by_path
class InvalidCacheBackendError(ImproperlyConfigured):
@@ -40,9 +40,7 @@ def get_key_func(key_func):
if callable(key_func):
return key_func
else:
- key_func_module_path, key_func_name = key_func.rsplit('.', 1)
- key_func_module = import_module(key_func_module_path)
- return getattr(key_func_module, key_func_name)
+ return import_by_path(key_func)
return default_key_func
20 django/core/files/storage.py
View
@@ -8,12 +8,12 @@
from datetime import datetime
from django.conf import settings
-from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
+from django.core.exceptions import SuspiciousOperation
from django.core.files import locks, File
from django.core.files.move import file_move_safe
from django.utils.encoding import force_text, filepath_to_uri
from django.utils.functional import LazyObject
-from django.utils.importlib import import_module
+from django.utils.module_loading import import_by_path
from django.utils.text import get_valid_filename
from django.utils._os import safe_join, abspathu
@@ -277,21 +277,7 @@ def modified_time(self, name):
return datetime.fromtimestamp(os.path.getmtime(self.path(name)))
def get_storage_class(import_path=None):
- if import_path is None:
- import_path = settings.DEFAULT_FILE_STORAGE
- try:
- dot = import_path.rindex('.')
- except ValueError:
- raise ImproperlyConfigured("%s isn't a storage module." % import_path)
- module, classname = import_path[:dot], import_path[dot+1:]
- try:
- mod = import_module(module)
- except ImportError as e:
- raise ImproperlyConfigured('Error importing storage module %s: "%s"' % (module, e))
- try:
- return getattr(mod, classname)
- except AttributeError:
- raise ImproperlyConfigured('Storage module "%s" does not define a "%s" class.' % (module, classname))
+ return import_by_path(import_path or settings.DEFAULT_FILE_STORAGE)
class DefaultStorage(LazyObject):
def _setup(self):
18 django/core/files/uploadhandler.py
View
@@ -7,10 +7,9 @@
from io import BytesIO
from django.conf import settings
-from django.core.exceptions import ImproperlyConfigured
from django.core.files.uploadedfile import TemporaryUploadedFile, InMemoryUploadedFile
-from django.utils import importlib
from django.utils.encoding import python_2_unicode_compatible
+from django.utils.module_loading import import_by_path
__all__ = ['UploadFileException','StopUpload', 'SkipFile', 'FileUploadHandler',
'TemporaryFileUploadHandler', 'MemoryFileUploadHandler',
@@ -201,17 +200,4 @@ def load_handler(path, *args, **kwargs):
<TemporaryFileUploadHandler object at 0x...>
"""
- i = path.rfind('.')
- module, attr = path[:i], path[i+1:]
- try:
- mod = importlib.import_module(module)
- except ImportError as e:
- raise ImproperlyConfigured('Error importing upload handler module %s: "%s"' % (module, e))
- except ValueError:
- raise ImproperlyConfigured('Error importing upload handler module.'
- 'Is FILE_UPLOAD_HANDLERS a correctly defined list or tuple?')
- try:
- cls = getattr(mod, attr)
- except AttributeError:
- raise ImproperlyConfigured('Module "%s" does not define a "%s" upload handler backend' % (module, attr))
- return cls(*args, **kwargs)
+ return import_by_path(path)(*args, **kwargs)
20 django/core/handlers/base.py
View
@@ -9,8 +9,9 @@
from django.core import exceptions
from django.core import urlresolvers
from django.core import signals
+from django.core.exceptions import MiddlewareNotUsed, PermissionDenied
from django.utils.encoding import force_text
-from django.utils.importlib import import_module
+from django.utils.module_loading import import_by_path
from django.utils import six
from django.views import debug
@@ -43,21 +44,10 @@ def load_middleware(self):
request_middleware = []
for middleware_path in settings.MIDDLEWARE_CLASSES:
- try:
- mw_module, mw_classname = middleware_path.rsplit('.', 1)
- except ValueError:
- raise exceptions.ImproperlyConfigured('%s isn\'t a middleware module' % middleware_path)
- try:
- mod = import_module(mw_module)
- except ImportError as e:
- raise exceptions.ImproperlyConfigured('Error importing middleware %s: "%s"' % (mw_module, e))
- try:
- mw_class = getattr(mod, mw_classname)
- except AttributeError:
- raise exceptions.ImproperlyConfigured('Middleware module "%s" does not define a "%s" class' % (mw_module, mw_classname))
+ mw_class = import_by_path(middleware_path)
try:
mw_instance = mw_class()
- except exceptions.MiddlewareNotUsed:
+ except MiddlewareNotUsed:
continue
if hasattr(mw_instance, 'process_request'):
@@ -154,7 +144,7 @@ def get_response(self, request):
except:
signals.got_request_exception.send(sender=self.__class__, request=request)
response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
- except exceptions.PermissionDenied:
+ except PermissionDenied:
logger.warning(
'Forbidden (Permission denied): %s', request.path,
extra={
16 django/core/mail/__init__.py
View
@@ -4,8 +4,7 @@
from __future__ import unicode_literals
from django.conf import settings
-from django.core.exceptions import ImproperlyConfigured
-from django.utils.importlib import import_module
+from django.utils.module_loading import import_by_path
# Imported for backwards compatibility, and for the sake
# of a cleaner namespace. These symbols used to be in
@@ -27,18 +26,7 @@ def get_connection(backend=None, fail_silently=False, **kwds):
Both fail_silently and other keyword arguments are used in the
constructor of the backend.
"""
- path = backend or settings.EMAIL_BACKEND
- try:
- mod_name, klass_name = path.rsplit('.', 1)
- mod = import_module(mod_name)
- except ImportError as e:
- raise ImproperlyConfigured(('Error importing email backend module %s: "%s"'
- % (mod_name, e)))
- try:
- klass = getattr(mod, klass_name)
- except AttributeError:
- raise ImproperlyConfigured(('Module "%s" does not define a '
- '"%s" class' % (mod_name, klass_name)))
+ klass = import_by_path(backend or settings.EMAIL_BACKEND)
return klass(fail_silently=fail_silently, **kwds)
25 django/core/servers/basehttp.py
View
@@ -21,11 +21,9 @@
from wsgiref import simple_server
from wsgiref.util import FileWrapper # for backwards compatibility
-import django
-from django.core.exceptions import ImproperlyConfigured
from django.core.management.color import color_style
from django.core.wsgi import get_wsgi_application
-from django.utils.importlib import import_module
+from django.utils.module_loading import import_by_path
__all__ = ['WSGIServer', 'WSGIRequestHandler']
@@ -49,22 +47,11 @@ def get_internal_wsgi_application():
app_path = getattr(settings, 'WSGI_APPLICATION')
if app_path is None:
return get_wsgi_application()
- module_name, attr = app_path.rsplit('.', 1)
- try:
- mod = import_module(module_name)
- except ImportError as e:
- raise ImproperlyConfigured(
- "WSGI application '%s' could not be loaded; "
- "could not import module '%s': %s" % (app_path, module_name, e))
- try:
- app = getattr(mod, attr)
- except AttributeError as e:
- raise ImproperlyConfigured(
- "WSGI application '%s' could not be loaded; "
- "can't find '%s' in module '%s': %s"
- % (app_path, attr, module_name, e))
-
- return app
+
+ return import_by_path(
+ app_path,
+ error_prefix="WSGI application '%s' could not be loaded; " % app_path
+ )
class WSGIServerException(Exception):
16 django/core/signing.py
View
@@ -41,11 +41,10 @@
import zlib
from django.conf import settings
-from django.core.exceptions import ImproperlyConfigured
from django.utils import baseconv
from django.utils.crypto import constant_time_compare, salted_hmac
from django.utils.encoding import force_bytes, force_str, force_text
-from django.utils.importlib import import_module
+from django.utils.module_loading import import_by_path
class BadSignature(Exception):
@@ -76,18 +75,7 @@ def base64_hmac(salt, value, key):
def get_cookie_signer(salt='django.core.signing.get_cookie_signer'):
- modpath = settings.SIGNING_BACKEND
- module, attr = modpath.rsplit('.', 1)
- try:
- mod = import_module(module)
- except ImportError as e:
- raise ImproperlyConfigured(
- 'Error importing cookie signer %s: "%s"' % (modpath, e))
- try:
- Signer = getattr(mod, attr)
- except AttributeError as e:
- raise ImproperlyConfigured(
- 'Error importing cookie signer %s: "%s"' % (modpath, e))
+ Signer = import_by_path(settings.SIGNING_BACKEND)
return Signer('django.http.cookies' + settings.SECRET_KEY, salt=salt)
13 django/db/utils.py
View
@@ -5,6 +5,7 @@
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.importlib import import_module
+from django.utils.module_loading import import_by_path
from django.utils._os import upath
from django.utils import six
@@ -110,17 +111,7 @@ def __init__(self, routers):
self.routers = []
for r in routers:
if isinstance(r, six.string_types):
- try:
- module_name, klass_name = r.rsplit('.', 1)
- module = import_module(module_name)
- except ImportError as e:
- raise ImproperlyConfigured('Error importing database router %s: "%s"' % (klass_name, e))
- try:
- router_class = getattr(module, klass_name)
- except AttributeError:
- raise ImproperlyConfigured('Module "%s" does not define a database router name "%s"' % (module, klass_name))
- else:
- router = router_class()
+ router = import_by_path(r)()
else:
router = r
self.routers.append(router)
14 django/template/context.py
View
@@ -1,6 +1,5 @@
from copy import copy
-from django.core.exceptions import ImproperlyConfigured
-from django.utils.importlib import import_module
+from django.utils.module_loading import import_by_path
# Cache of actual callables.
_standard_context_processors = None
@@ -146,16 +145,7 @@ def get_standard_processors():
collect.extend(_builtin_context_processors)
collect.extend(settings.TEMPLATE_CONTEXT_PROCESSORS)
for path in collect:
- i = path.rfind('.')
- module, attr = path[:i], path[i+1:]
- try:
- mod = import_module(module)
- except ImportError as e:
- raise ImproperlyConfigured('Error importing request processor module %s: "%s"' % (module, e))
- try:
- func = getattr(mod, attr)
- except AttributeError:
- raise ImproperlyConfigured('Module "%s" does not define a "%s" callable request processor' % (module, attr))
+ func = import_by_path(path)
processors.append(func)
_standard_context_processors = tuple(processors)
return _standard_context_processors
12 django/template/loader.py
View
@@ -27,8 +27,8 @@
from django.core.exceptions import ImproperlyConfigured
from django.template.base import Origin, Template, Context, TemplateDoesNotExist, add_to_builtins
-from django.utils.importlib import import_module
from django.conf import settings
+from django.utils.module_loading import import_by_path
from django.utils import six
template_source_loaders = None
@@ -91,15 +91,7 @@ def find_template_loader(loader):
else:
args = []
if isinstance(loader, six.string_types):
- module, attr = loader.rsplit('.', 1)
- try:
- mod = import_module(module)
- except ImportError as e:
- raise ImproperlyConfigured('Error importing template source loader %s: "%s"' % (loader, e))
- try:
- TemplateLoader = getattr(mod, attr)
- except AttributeError as e:
- raise ImproperlyConfigured('Error importing template source loader %s: "%s"' % (loader, e))
+ TemplateLoader = import_by_path(loader)
if hasattr(TemplateLoader, 'load_template_source'):
func = TemplateLoader(*args)
26 django/utils/module_loading.py
View
@@ -2,6 +2,32 @@
import os
import sys
+from django.core.exceptions import ImproperlyConfigured
+from django.utils.importlib import import_module
+
+
+def import_by_path(dotted_path, error_prefix=''):
+ """
+ Import a dotted module path and return the attribute/class designated by the
+ last name in the path. Raise ImproperlyConfigured if something goes wrong.
+ """
+ try:
+ module_path, class_name = dotted_path.rsplit('.', 1)
+ except ValueError:
+ raise ImproperlyConfigured("%s%s doesn't look like a module path" % (
+ error_prefix, dotted_path))
+ try:
+ module = import_module(module_path)
+ except ImportError as e:
+ raise ImproperlyConfigured('%sError importing module %s: "%s"' % (
+ error_prefix, module_path, e))
+ try:
+ attr = getattr(module, class_name)
+ except AttributeError:
+ raise ImproperlyConfigured('%sModule "%s" does not define a "%s" attribute/class' % (
+ error_prefix, module_path, class_name))
+ return attr
+
def module_has_submodule(package, module_name):
"""See if 'module' is in 'package'."""
15 django/views/debug.py
View
@@ -13,8 +13,8 @@
from django.template import Template, Context, TemplateDoesNotExist
from django.template.defaultfilters import force_escape, pprint
from django.utils.html import escape
-from django.utils.importlib import import_module
from django.utils.encoding import force_bytes, smart_text
+from django.utils.module_loading import import_by_path
from django.utils import six
HIDDEN_SETTINGS = re.compile('API|TOKEN|KEY|SECRET|PASS|PROFANITIES_LIST|SIGNATURE')
@@ -76,17 +76,8 @@ def get_exception_reporter_filter(request):
global default_exception_reporter_filter
if default_exception_reporter_filter is None:
# Load the default filter for the first time and cache it.
- modpath = settings.DEFAULT_EXCEPTION_REPORTER_FILTER
- modname, classname = modpath.rsplit('.', 1)
- try:
- mod = import_module(modname)
- except ImportError as e:
- raise ImproperlyConfigured(
- 'Error importing default exception reporter filter %s: "%s"' % (modpath, e))
- try:
- default_exception_reporter_filter = getattr(mod, classname)()
- except AttributeError:
- raise ImproperlyConfigured('Default exception reporter filter module "%s" does not define a "%s" class' % (modname, classname))
+ default_exception_reporter_filter = import_by_path(
+ settings.DEFAULT_EXCEPTION_REPORTER_FILTER)()
if request:
return getattr(request, 'exception_reporter_filter', default_exception_reporter_filter)
else:
12 tests/regressiontests/file_storage/tests.py
View
@@ -57,9 +57,9 @@ def test_get_invalid_storage_module(self):
"""
self.assertRaisesMessage(
ImproperlyConfigured,
- "NonExistingStorage isn't a storage module.",
+ "Error importing module storage: \"No module named storage\"",
get_storage_class,
- 'NonExistingStorage')
+ 'storage.NonExistingStorage')
def test_get_nonexisting_storage_class(self):
"""
@@ -67,8 +67,8 @@ def test_get_nonexisting_storage_class(self):
"""
self.assertRaisesMessage(
ImproperlyConfigured,
- 'Storage module "django.core.files.storage" does not define a '\
- '"NonExistingStorage" class.',
+ 'Module "django.core.files.storage" does not define a '
+ '"NonExistingStorage" attribute/class',
get_storage_class,
'django.core.files.storage.NonExistingStorage')
@@ -79,8 +79,8 @@ def test_get_nonexisting_storage_module(self):
# Error message may or may not be the fully qualified path.
six.assertRaisesRegex(self,
ImproperlyConfigured,
- ('Error importing storage module django.core.files.non_existing_'
- 'storage: "No module named .*non_existing_storage'),
+ 'Error importing module django.core.files.non_existing_storage: '
+ '"No module named non_existing_storage"',
get_storage_class,
'django.core.files.non_existing_storage.NonExistingStorage'
)
20 tests/regressiontests/utils/module_loading.py
View
@@ -3,9 +3,10 @@
import imp
from zipimport import zipimporter
+from django.core.exceptions import ImproperlyConfigured
from django.utils import unittest
from django.utils.importlib import import_module
-from django.utils.module_loading import module_has_submodule
+from django.utils.module_loading import import_by_path, module_has_submodule
from django.utils._os import upath
@@ -103,6 +104,23 @@ def test_deep_loader(self):
self.assertFalse(module_has_submodule(egg_module, 'no_such_module'))
self.assertRaises(ImportError, import_module, 'egg_module.sub1.sub2.no_such_module')
+
+class ModuleImportTestCase(unittest.TestCase):
+ def test_import_by_path(self):
+ cls = import_by_path(
+ 'django.utils.module_loading.import_by_path')
+ self.assertEqual(cls, import_by_path)
+
+ # Test exceptions raised
+ for path in ('no_dots_in_path', 'unexistent.path',
+ 'tests.regressiontests.utils.unexistent'):
+ self.assertRaises(ImproperlyConfigured, import_by_path, path)
+
+ with self.assertRaises(ImproperlyConfigured) as cm:
+ import_by_path('unexistent.module.path', error_prefix="Foo")
+ self.assertTrue(str(cm.exception).startswith('Foo'))
+
+
class ProxyFinder(object):
def __init__(self):
self._cache = {}
3  tests/regressiontests/utils/tests.py
View
@@ -20,7 +20,8 @@
from .http import TestUtilsHttp, ETagProcessingTests, HttpDateProcessingTests
from .ipv6 import TestUtilsIPv6
from .jslex import JsToCForGettextTest, JsTokensTest
-from .module_loading import CustomLoader, DefaultLoader, EggLoader
+from .module_loading import (CustomLoader, DefaultLoader, EggLoader,
+ ModuleImportTestCase)
from .numberformat import TestNumberFormat
from .os_utils import SafeJoinTests
from .regex_helper import NormalizeTests
4 tests/regressiontests/wsgi/tests.py
View
@@ -85,7 +85,7 @@ def mock_get_wsgi_app():
def test_bad_module(self):
with six.assertRaisesRegex(self,
ImproperlyConfigured,
- r"^WSGI application 'regressiontests.wsgi.noexist.app' could not be loaded; could not import module 'regressiontests.wsgi.noexist':"):
+ r"^WSGI application 'regressiontests.wsgi.noexist.app' could not be loaded; Error importing.*"):
get_internal_wsgi_application()
@@ -94,6 +94,6 @@ def test_bad_module(self):
def test_bad_name(self):
with six.assertRaisesRegex(self,
ImproperlyConfigured,
- r"^WSGI application 'regressiontests.wsgi.wsgi.noexist' could not be loaded; can't find 'noexist' in module 'regressiontests.wsgi.wsgi':"):
+ r"^WSGI application 'regressiontests.wsgi.wsgi.noexist' could not be loaded; Module.*"):
get_internal_wsgi_application()

5 comments on commit 7c5b244

Jannis Leidel
Owner

ZOMG YES!!!

Marc Tamlyn
Collaborator

Should (or could) this be considered a public API now?

Sébastien Leblanc

Wow, what a cleanup!

Claude Paroz
Collaborator

Should (or could) this be considered a public API now?

Not until it is documented on https://docs.djangoproject.com/en/dev/ref/utils/. However, feel free to open a Trac ticket requesting it to be documented (hence public API), if you think it's really useful.

Marc Tamlyn
Collaborator

I know of a lot of libraries which utilise this sort of functionality for their own settings. Opened https://code.djangoproject.com/ticket/19748

Please sign in to comment.
Something went wrong with that request. Please try again.