summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBerker Peksag <berker.peksag@gmail.com>2014-01-20 22:15:14 +0200
committerTim Graham <timograham@gmail.com>2014-02-08 11:12:19 -0500
commit5d263dee304fdaf95e18d2f0619d6925984a7f02 (patch)
tree764bc0cd21bca796d9f1bd0b8d2645a9773e48de
parentfcc21837dc691ba6465853c0b68927cb075a4591 (diff)
Fixed #21674 -- Deprecated the import_by_path() function in favor of import_string().
Thanks Aymeric Augustin for the suggestion and review.
-rw-r--r--AUTHORS1
-rw-r--r--django/contrib/admin/tests.py4
-rw-r--r--django/contrib/auth/__init__.py4
-rw-r--r--django/contrib/auth/hashers.py4
-rw-r--r--django/contrib/auth/middleware.py2
-rw-r--r--django/contrib/formtools/wizard/storage/__init__.py7
-rw-r--r--django/contrib/messages/storage/__init__.py6
-rw-r--r--django/contrib/sessions/backends/base.py4
-rw-r--r--django/contrib/staticfiles/finders.py4
-rw-r--r--django/core/cache/__init__.py10
-rw-r--r--django/core/cache/backends/base.py4
-rw-r--r--django/core/files/storage.py4
-rw-r--r--django/core/files/uploadhandler.py4
-rw-r--r--django/core/handlers/base.py4
-rw-r--r--django/core/mail/__init__.py4
-rw-r--r--django/core/servers/basehttp.py20
-rw-r--r--django/core/signing.py4
-rw-r--r--django/db/migrations/state.py8
-rw-r--r--django/db/utils.py4
-rw-r--r--django/template/context.py4
-rw-r--r--django/template/loader.py4
-rw-r--r--django/utils/log.py4
-rw-r--r--django/utils/module_loading.py37
-rw-r--r--django/views/debug.py4
-rw-r--r--docs/internals/deprecation.txt3
-rw-r--r--docs/ref/utils.txt23
-rw-r--r--docs/releases/1.7.txt11
-rw-r--r--tests/file_storage/tests.py18
-rw-r--r--tests/staticfiles_tests/tests.py4
-rw-r--r--tests/utils_tests/test_module_loading.py32
-rw-r--r--tests/wsgi/tests.py2
31 files changed, 154 insertions, 94 deletions
diff --git a/AUTHORS b/AUTHORS
index f3f300889a..fe1ede2f54 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -484,6 +484,7 @@ answer newbie questions, and generally made Django that much better:
John Paulett <john@paulett.org>
pavithran s <pavithran.s@gmail.com>
Barry Pederson <bp@barryp.org>
+ Berker Peksag <berker.peksag@gmail.com>
Andreas Pelme <andreas@pelme.se>
permonik@mesias.brnonet.cz
peter@mymart.com
diff --git a/django/contrib/admin/tests.py b/django/contrib/admin/tests.py
index 40fd10089b..56b8b66023 100644
--- a/django/contrib/admin/tests.py
+++ b/django/contrib/admin/tests.py
@@ -2,7 +2,7 @@ import os
from unittest import SkipTest
from django.contrib.staticfiles.testing import StaticLiveServerCase
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
from django.utils.translation import ugettext as _
@@ -22,7 +22,7 @@ class AdminSeleniumWebDriverTestCase(StaticLiveServerCase):
if not os.environ.get('DJANGO_SELENIUM_TESTS', False):
raise SkipTest('Selenium tests not requested')
try:
- cls.selenium = import_by_path(cls.webdriver_class)()
+ cls.selenium = import_string(cls.webdriver_class)()
except Exception as e:
raise SkipTest('Selenium webdriver "%s" not installed or not '
'operational: %s' % (cls.webdriver_class, str(e)))
diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py
index 9fd46814b2..ed01da94cf 100644
--- a/django/contrib/auth/__init__.py
+++ b/django/contrib/auth/__init__.py
@@ -4,7 +4,7 @@ import re
from django.apps import apps as django_apps
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
from django.middleware.csrf import rotate_token
from .signals import user_logged_in, user_logged_out, user_login_failed
@@ -15,7 +15,7 @@ REDIRECT_FIELD_NAME = 'next'
def load_backend(path):
- return import_by_path(path)()
+ return import_string(path)()
def get_backends():
diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py
index 713240b502..992e19e2a4 100644
--- a/django/contrib/auth/hashers.py
+++ b/django/contrib/auth/hashers.py
@@ -13,7 +13,7 @@ from django.utils.encoding import force_bytes, force_str, force_text
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.module_loading import import_string
from django.utils.translation import ugettext_noop as _
@@ -92,7 +92,7 @@ def load_hashers(password_hashers=None):
if not password_hashers:
password_hashers = settings.PASSWORD_HASHERS
for backend in password_hashers:
- hasher = import_by_path(backend)()
+ hasher = import_string(backend)()
if not getattr(hasher, 'algorithm'):
raise ImproperlyConfigured("hasher doesn't specify an "
"algorithm name: %s" % backend)
diff --git a/django/contrib/auth/middleware.py b/django/contrib/auth/middleware.py
index a107055e34..3ee742446d 100644
--- a/django/contrib/auth/middleware.py
+++ b/django/contrib/auth/middleware.py
@@ -58,7 +58,7 @@ class RemoteUserMiddleware(object):
auth.BACKEND_SESSION_KEY, ''))
if isinstance(stored_backend, RemoteUserBackend):
auth.logout(request)
- except ImproperlyConfigured:
+ except ImportError:
# backend failed to load
auth.logout(request)
return
diff --git a/django/contrib/formtools/wizard/storage/__init__.py b/django/contrib/formtools/wizard/storage/__init__.py
index 4dbc15a298..71a21f89a3 100644
--- a/django/contrib/formtools/wizard/storage/__init__.py
+++ b/django/contrib/formtools/wizard/storage/__init__.py
@@ -1,5 +1,4 @@
-from django.core.exceptions import ImproperlyConfigured
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
from django.contrib.formtools.wizard.storage.base import BaseStorage
from django.contrib.formtools.wizard.storage.exceptions import (
@@ -12,7 +11,7 @@ __all__ = [
def get_storage(path, *args, **kwargs):
try:
- storage_class = import_by_path(path)
- except ImproperlyConfigured as e:
+ storage_class = import_string(path)
+ except ImportError as e:
raise MissingStorage('Error loading storage: %s' % e)
return storage_class(*args, **kwargs)
diff --git a/django/contrib/messages/storage/__init__.py b/django/contrib/messages/storage/__init__.py
index 591ae14d7c..373dbbaada 100644
--- a/django/contrib/messages/storage/__init__.py
+++ b/django/contrib/messages/storage/__init__.py
@@ -1,12 +1,12 @@
from django.conf import settings
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
def default_storage(request):
"""
Callable with the same interface as the storage classes.
- This isn't just default_storage = import_by_path(settings.MESSAGE_STORAGE)
+ This isn't just default_storage = import_string(settings.MESSAGE_STORAGE)
to avoid accessing the settings at the module level.
"""
- return import_by_path(settings.MESSAGE_STORAGE)(request)
+ return import_string(settings.MESSAGE_STORAGE)(request)
diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py
index 2a497fe9a1..a77a25bb31 100644
--- a/django/contrib/sessions/backends/base.py
+++ b/django/contrib/sessions/backends/base.py
@@ -12,7 +12,7 @@ from django.utils.crypto import get_random_string
from django.utils.crypto import salted_hmac
from django.utils import timezone
from django.utils.encoding import force_bytes, force_text
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
from django.contrib.sessions.exceptions import SuspiciousSession
@@ -40,7 +40,7 @@ class SessionBase(object):
self._session_key = session_key
self.accessed = False
self.modified = False
- self.serializer = import_by_path(settings.SESSION_SERIALIZER)
+ self.serializer = import_string(settings.SESSION_SERIALIZER)
def __contains__(self, key):
return key in self._session
diff --git a/django/contrib/staticfiles/finders.py b/django/contrib/staticfiles/finders.py
index 30767405d3..86b620696f 100644
--- a/django/contrib/staticfiles/finders.py
+++ b/django/contrib/staticfiles/finders.py
@@ -6,7 +6,7 @@ from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.files.storage import default_storage, Storage, FileSystemStorage
from django.utils.functional import empty, LazyObject
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
from django.utils._os import safe_join
from django.utils import six, lru_cache
@@ -257,7 +257,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.
"""
- Finder = import_by_path(import_path)
+ Finder = import_string(import_path)
if not issubclass(Finder, BaseFinder):
raise ImproperlyConfigured('Finder "%s" is not a subclass of "%s"' %
(Finder, BaseFinder))
diff --git a/django/core/cache/__init__.py b/django/core/cache/__init__.py
index c362800dcc..603086a39c 100644
--- a/django/core/cache/__init__.py
+++ b/django/core/cache/__init__.py
@@ -20,7 +20,7 @@ from django.core import signals
from django.core.cache.backends.base import (
InvalidCacheBackendError, CacheKeyWarning, BaseCache)
from django.core.exceptions import ImproperlyConfigured
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
__all__ = [
@@ -69,8 +69,8 @@ def _create_cache(backend, **kwargs):
except KeyError:
try:
# Trying to import the given backend, in case it's a dotted path
- import_by_path(backend)
- except ImproperlyConfigured as e:
+ import_string(backend)
+ except ImportError as e:
raise InvalidCacheBackendError("Could not find backend '%s': %s" % (
backend, e))
location = kwargs.pop('LOCATION', '')
@@ -80,8 +80,8 @@ def _create_cache(backend, **kwargs):
params.update(kwargs)
backend = params.pop('BACKEND')
location = params.pop('LOCATION', '')
- backend_cls = import_by_path(backend)
- except (AttributeError, ImportError, ImproperlyConfigured) as e:
+ backend_cls = import_string(backend)
+ except ImportError as e:
raise InvalidCacheBackendError(
"Could not find backend '%s': %s" % (backend, e))
return backend_cls(location, params)
diff --git a/django/core/cache/backends/base.py b/django/core/cache/backends/base.py
index 2c3151369a..ae83865db8 100644
--- a/django/core/cache/backends/base.py
+++ b/django/core/cache/backends/base.py
@@ -5,7 +5,7 @@ import time
import warnings
from django.core.exceptions import ImproperlyConfigured, DjangoRuntimeWarning
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
class InvalidCacheBackendError(ImproperlyConfigured):
@@ -45,7 +45,7 @@ def get_key_func(key_func):
if callable(key_func):
return key_func
else:
- return import_by_path(key_func)
+ return import_string(key_func)
return default_key_func
diff --git a/django/core/files/storage.py b/django/core/files/storage.py
index c8b4c7185d..ade1817dad 100644
--- a/django/core/files/storage.py
+++ b/django/core/files/storage.py
@@ -9,7 +9,7 @@ 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.module_loading import import_by_path
+from django.utils.module_loading import import_string
from django.utils.six.moves.urllib.parse import urljoin
from django.utils.text import get_valid_filename
from django.utils._os import safe_join, abspathu
@@ -301,7 +301,7 @@ class FileSystemStorage(Storage):
def get_storage_class(import_path=None):
- return import_by_path(import_path or settings.DEFAULT_FILE_STORAGE)
+ return import_string(import_path or settings.DEFAULT_FILE_STORAGE)
class DefaultStorage(LazyObject):
diff --git a/django/core/files/uploadhandler.py b/django/core/files/uploadhandler.py
index 995e97a2f6..2fa1d7a934 100644
--- a/django/core/files/uploadhandler.py
+++ b/django/core/files/uploadhandler.py
@@ -9,7 +9,7 @@ from io import BytesIO
from django.conf import settings
from django.core.files.uploadedfile import TemporaryUploadedFile, InMemoryUploadedFile
from django.utils.encoding import python_2_unicode_compatible
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
__all__ = [
'UploadFileException', 'StopUpload', 'SkipFile', 'FileUploadHandler',
@@ -214,4 +214,4 @@ def load_handler(path, *args, **kwargs):
<TemporaryFileUploadHandler object at 0x...>
"""
- return import_by_path(path)(*args, **kwargs)
+ return import_string(path)(*args, **kwargs)
diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py
index 587cbae2c3..3986257660 100644
--- a/django/core/handlers/base.py
+++ b/django/core/handlers/base.py
@@ -11,7 +11,7 @@ from django.core import signals
from django.core.exceptions import MiddlewareNotUsed, PermissionDenied, SuspiciousOperation
from django.db import connections, transaction
from django.utils.encoding import force_text
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
from django.utils import six
from django.views import debug
@@ -43,7 +43,7 @@ class BaseHandler(object):
request_middleware = []
for middleware_path in settings.MIDDLEWARE_CLASSES:
- mw_class = import_by_path(middleware_path)
+ mw_class = import_string(middleware_path)
try:
mw_instance = mw_class()
except MiddlewareNotUsed:
diff --git a/django/core/mail/__init__.py b/django/core/mail/__init__.py
index 5a01adc4ea..cef9d1b87b 100644
--- a/django/core/mail/__init__.py
+++ b/django/core/mail/__init__.py
@@ -4,7 +4,7 @@ Tools for sending email.
from __future__ import unicode_literals
from django.conf import settings
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
# Imported for backwards compatibility, and for the sake
# of a cleaner namespace. These symbols used to be in
@@ -34,7 +34,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.
"""
- klass = import_by_path(backend or settings.EMAIL_BACKEND)
+ klass = import_string(backend or settings.EMAIL_BACKEND)
return klass(fail_silently=fail_silently, **kwds)
diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py
index d34c246d51..090a694c3e 100644
--- a/django/core/servers/basehttp.py
+++ b/django/core/servers/basehttp.py
@@ -16,9 +16,11 @@ import traceback
from wsgiref import simple_server
from wsgiref.util import FileWrapper # NOQA: for backwards compatibility
+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.module_loading import import_by_path
+from django.utils import six
+from django.utils.module_loading import import_string
from django.utils.six.moves import socketserver
__all__ = ('WSGIServer', 'WSGIRequestHandler', 'MAX_SOCKET_CHUNK_SIZE')
@@ -50,10 +52,18 @@ def get_internal_wsgi_application():
if app_path is None:
return get_wsgi_application()
- return import_by_path(
- app_path,
- error_prefix="WSGI application '%s' could not be loaded; " % app_path
- )
+ try:
+ return import_string(app_path)
+ except ImportError as e:
+ msg = (
+ "WSGI application '%(app_path)s' could not be loaded; "
+ "Error importing module: '%(exception)s'" % ({
+ 'app_path': app_path,
+ 'exception': e,
+ })
+ )
+ six.reraise(ImproperlyConfigured, ImproperlyConfigured(msg),
+ sys.exc_info()[2])
class ServerHandler(simple_server.ServerHandler, object):
diff --git a/django/core/signing.py b/django/core/signing.py
index 15b9eaec6e..17953af151 100644
--- a/django/core/signing.py
+++ b/django/core/signing.py
@@ -44,7 +44,7 @@ from django.conf import settings
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.module_loading import import_by_path
+from django.utils.module_loading import import_string
class BadSignature(Exception):
@@ -75,7 +75,7 @@ def base64_hmac(salt, value, key):
def get_cookie_signer(salt='django.core.signing.get_cookie_signer'):
- Signer = import_by_path(settings.SIGNING_BACKEND)
+ Signer = import_string(settings.SIGNING_BACKEND)
return Signer('django.http.cookies' + settings.SECRET_KEY, salt=salt)
diff --git a/django/db/migrations/state.py b/django/db/migrations/state.py
index 949d2251e5..3e73744091 100644
--- a/django/db/migrations/state.py
+++ b/django/db/migrations/state.py
@@ -3,7 +3,7 @@ from django.apps.registry import Apps
from django.db import models
from django.db.models.options import DEFAULT_NAMES, normalize_unique_together
from django.utils import six
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
class InvalidBasesError(ValueError):
@@ -115,7 +115,7 @@ class ModelState(object):
fields = []
for field in model._meta.local_fields:
name, path, args, kwargs = field.deconstruct()
- field_class = import_by_path(path)
+ field_class = import_string(path)
try:
fields.append((name, field_class(*args, **kwargs)))
except TypeError as e:
@@ -127,7 +127,7 @@ class ModelState(object):
))
for field in model._meta.local_many_to_many:
name, path, args, kwargs = field.deconstruct()
- field_class = import_by_path(path)
+ field_class = import_string(path)
try:
fields.append((name, field_class(*args, **kwargs)))
except TypeError as e:
@@ -175,7 +175,7 @@ class ModelState(object):
fields = []
for name, field in self.fields:
_, path, args, kwargs = field.deconstruct()
- field_class = import_by_path(path)
+ field_class = import_string(path)
fields.append((name, field_class(*args, **kwargs)))
# Now make a copy
return self.__class__(
diff --git a/django/db/utils.py b/django/db/utils.py
index 0bcf33f252..51d2ffcba0 100644
--- a/django/db/utils.py
+++ b/django/db/utils.py
@@ -7,7 +7,7 @@ import warnings
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.functional import cached_property
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
from django.utils._os import upath
from django.utils import six
@@ -221,7 +221,7 @@ class ConnectionRouter(object):
routers = []
for r in self._routers:
if isinstance(r, six.string_types):
- router = import_by_path(r)()
+ router = import_string(r)()
else:
router = r
routers.append(router)
diff --git a/django/template/context.py b/django/template/context.py
index 774f7af6b1..0f624ff8a6 100644
--- a/django/template/context.py
+++ b/django/template/context.py
@@ -1,5 +1,5 @@
from copy import copy
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
# Cache of actual callables.
_standard_context_processors = None
@@ -162,7 +162,7 @@ def get_standard_processors():
collect.extend(_builtin_context_processors)
collect.extend(settings.TEMPLATE_CONTEXT_PROCESSORS)
for path in collect:
- func = import_by_path(path)
+ func = import_string(path)
processors.append(func)
_standard_context_processors = tuple(processors)
return _standard_context_processors
diff --git a/django/template/loader.py b/django/template/loader.py
index 1e18eecc02..e9e2172e4b 100644
--- a/django/template/loader.py
+++ b/django/template/loader.py
@@ -28,7 +28,7 @@
from django.core.exceptions import ImproperlyConfigured
from django.template.base import Origin, Template, Context, TemplateDoesNotExist
from django.conf import settings
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
from django.utils import six
template_source_loaders = None
@@ -95,7 +95,7 @@ def find_template_loader(loader):
else:
args = []
if isinstance(loader, six.string_types):
- TemplateLoader = import_by_path(loader)
+ TemplateLoader = import_string(loader)
if hasattr(TemplateLoader, 'load_template_source'):
func = TemplateLoader(*args)
diff --git a/django/utils/log.py b/django/utils/log.py
index cf778b619b..f319704116 100644
--- a/django/utils/log.py
+++ b/django/utils/log.py
@@ -5,7 +5,7 @@ import warnings
from django.conf import settings
from django.core import mail
from django.core.mail import get_connection
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
from django.views.debug import ExceptionReporter, get_exception_reporter_filter
# Imports kept for backwards-compatibility in Django 1.7.
@@ -73,7 +73,7 @@ def configure_logging(logging_config, logging_settings):
if logging_config:
# First find the logging configuration function ...
- logging_config_func = import_by_path(logging_config)
+ logging_config_func = import_string(logging_config)
logging_config_func(DEFAULT_LOGGING)
diff --git a/django/utils/module_loading.py b/django/utils/module_loading.py
index a6fe7aebcb..7ba0381307 100644
--- a/django/utils/module_loading.py
+++ b/django/utils/module_loading.py
@@ -5,33 +5,48 @@ import imp
from importlib import import_module
import os
import sys
+import warnings
from django.core.exceptions import ImproperlyConfigured
from django.utils import six
-def import_by_path(dotted_path, error_prefix=''):
+def import_string(dotted_path):
"""
Import a dotted module path and return the attribute/class designated by the
- last name in the path. Raise ImproperlyConfigured if something goes wrong.
+ last name in the path. Raise ImportError if the import failed.
"""
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))
+ msg = "%s doesn't look like a module path" % dotted_path
+ six.reraise(ImportError, ImportError(msg), sys.exc_info()[2])
+
+ module = import_module(module_path)
+
try:
- module = import_module(module_path)
+ return getattr(module, class_name)
+ except AttributeError:
+ msg = 'Module "%s" does not define a "%s" attribute/class' % (
+ dotted_path, class_name)
+ six.reraise(ImportError, ImportError(msg), sys.exc_info()[2])
+
+
+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.
+ """
+ warnings.warn(
+ 'import_by_path() has been deprecated. Use import_string() instead.',
+ PendingDeprecationWarning, stacklevel=2)
+ try:
+ attr = import_string(dotted_path)
except ImportError as e:
msg = '%sError importing module %s: "%s"' % (
- error_prefix, module_path, e)
+ error_prefix, dotted_path, e)
six.reraise(ImproperlyConfigured, ImproperlyConfigured(msg),
sys.exc_info()[2])
- 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
diff --git a/django/views/debug.py b/django/views/debug.py
index 4f2097600f..d665c4e36b 100644
--- a/django/views/debug.py
+++ b/django/views/debug.py
@@ -14,7 +14,7 @@ from django.template.defaultfilters import force_escape, pprint
from django.utils.datastructures import MultiValueDict
from django.utils.html import escape
from django.utils.encoding import force_bytes, smart_text
-from django.utils.module_loading import import_by_path
+from django.utils.module_loading import import_string
from django.utils import six
HIDDEN_SETTINGS = re.compile('API|TOKEN|KEY|SECRET|PASS|PROFANITIES_LIST|SIGNATURE')
@@ -85,7 +85,7 @@ 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.
- default_exception_reporter_filter = import_by_path(
+ default_exception_reporter_filter = import_string(
settings.DEFAULT_EXCEPTION_REPORTER_FILTER)()
if request:
return getattr(request, 'exception_reporter_filter', default_exception_reporter_filter)
diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt
index 7b5a5d7468..4b79e4696d 100644
--- a/docs/internals/deprecation.txt
+++ b/docs/internals/deprecation.txt
@@ -116,6 +116,9 @@ details on these changes.
* ``django.db.backends.DatabaseValidation.validate_field`` will be removed in
favor of the ``check_field`` method.
+* ``django.utils.module_loading.import_by_path`` will be removed in favor of
+ ``django.utils.module_loading.import_string``.
+
.. _deprecation-removed-in-1.8:
1.8
diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt
index 6cac9fa058..5313a1de19 100644
--- a/docs/ref/utils.txt
+++ b/docs/ref/utils.txt
@@ -709,22 +709,31 @@ escaping HTML.
Functions for working with Python modules.
-.. function:: import_by_path(dotted_path, error_prefix='')
+.. function:: import_string(dotted_path)
- .. versionadded:: 1.6
+ .. versionadded:: 1.7
Imports a dotted module path and returns the attribute/class designated by
- the last name in the path. Raises
- :exc:`~django.core.exceptions.ImproperlyConfigured` if something goes
- wrong. For example::
+ the last name in the path. Raises ``ImportError`` if the import failed. For
+ example::
- from django.utils.module_loading import import_by_path
- ImproperlyConfigured = import_by_path('django.core.exceptions.ImproperlyConfigured')
+ from django.utils.module_loading import import_string
+ ImproperlyConfigured = import_string('django.core.exceptions.ImproperlyConfigured')
is equivalent to::
from django.core.exceptions import ImproperlyConfigured
+.. function:: import_by_path(dotted_path, error_prefix='')
+
+ .. versionadded:: 1.6
+ .. deprecated:: 1.7
+ Use :meth:`~django.utils.module_loading.import_string` instead.
+
+ Imports a dotted module path and returns the attribute/class designated by
+ the last name in the path. Raises :exc:`~django.core.exceptions.ImproperlyConfigured`
+ if something goes wrong.
+
``django.utils.safestring``
===========================
diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt
index f9d3a7e795..1fe2b25ecf 100644
--- a/docs/releases/1.7.txt
+++ b/docs/releases/1.7.txt
@@ -1042,6 +1042,17 @@ Features deprecated in 1.7
respectively :mod:`logging.config` and :mod:`importlib` provided for Python
versions prior to 2.7. They have been deprecated.
+``django.utils.module_loading.import_by_path``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The current :meth:`~django.utils.module_loading.import_by_path` function
+catches ``AttributeError``, ``ImportError`` and ``ValueError`` exceptions,
+and re-raises :exc:`~django.core.exceptions.ImproperlyConfigured`. Such
+exception masking makes it needlessly hard to diagnose circular import
+problems, because it makes it look like the problem comes from inside Django.
+It has been deprecated in favor of
+:meth:`~django.utils.module_loading.import_string`.
+
``django.utils.tzinfo``
~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/file_storage/tests.py b/tests/file_storage/tests.py
index 2f047f049a..ae2076c5e4 100644
--- a/tests/file_storage/tests.py
+++ b/tests/file_storage/tests.py
@@ -16,7 +16,7 @@ except ImportError:
import dummy_threading as threading
from django.core.cache import cache
-from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
+from django.core.exceptions import SuspiciousOperation
from django.core.files.base import File, ContentFile
from django.core.files.storage import FileSystemStorage, get_storage_class
from django.core.files.uploadedfile import SimpleUploadedFile
@@ -43,29 +43,23 @@ class GetStorageClassTests(SimpleTestCase):
"""
get_storage_class raises an error if the requested import don't exist.
"""
- with six.assertRaisesRegex(self, ImproperlyConfigured,
- "Error importing module storage: \"No module named '?storage'?\""):
+ with six.assertRaisesRegex(self, ImportError, "No module named '?storage'?"):
get_storage_class('storage.NonExistingStorage')
def test_get_nonexisting_storage_class(self):
"""
get_storage_class raises an error if the requested class don't exist.
"""
- self.assertRaisesMessage(
- ImproperlyConfigured,
- 'Module "django.core.files.storage" does not define a '
- '"NonExistingStorage" attribute/class',
- get_storage_class,
- 'django.core.files.storage.NonExistingStorage')
+ self.assertRaises(ImportError, get_storage_class,
+ 'django.core.files.storage.NonExistingStorage')
def test_get_nonexisting_storage_module(self):
"""
get_storage_class raises an error if the requested module don't exist.
"""
# Error message may or may not be the fully qualified path.
- with six.assertRaisesRegex(self, ImproperlyConfigured,
- "Error importing module django.core.files.non_existing_storage: "
- "\"No module named '?(django.core.files.)?non_existing_storage'?\""):
+ with six.assertRaisesRegex(self, ImportError,
+ "No module named '?(django.core.files.)?non_existing_storage'?"):
get_storage_class(
'django.core.files.non_existing_storage.NonExistingStorage')
diff --git a/tests/staticfiles_tests/tests.py b/tests/staticfiles_tests/tests.py
index 74e3782c83..1d3daff11e 100644
--- a/tests/staticfiles_tests/tests.py
+++ b/tests/staticfiles_tests/tests.py
@@ -788,11 +788,11 @@ class TestMiscFinder(TestCase):
finders.FileSystemFinder)
def test_get_finder_bad_classname(self):
- self.assertRaises(ImproperlyConfigured, finders.get_finder,
+ self.assertRaises(ImportError, finders.get_finder,
'django.contrib.staticfiles.finders.FooBarFinder')
def test_get_finder_bad_module(self):
- self.assertRaises(ImproperlyConfigured,
+ self.assertRaises(ImportError,
finders.get_finder, 'foo.bar.FooBarFinder')
def test_cache(self):
diff --git a/tests/utils_tests/test_module_loading.py b/tests/utils_tests/test_module_loading.py
index 905163b1b9..2eb01ad770 100644
--- a/tests/utils_tests/test_module_loading.py
+++ b/tests/utils_tests/test_module_loading.py
@@ -3,13 +3,15 @@ from importlib import import_module
import os
import sys
import unittest
+import warnings
from zipimport import zipimporter
from django.core.exceptions import ImproperlyConfigured
from django.test import SimpleTestCase, modify_settings
-from django.test.utils import extend_sys_path
+from django.test.utils import IgnorePendingDeprecationWarningsMixin, extend_sys_path
from django.utils import six
-from django.utils.module_loading import autodiscover_modules, import_by_path, module_has_submodule
+from django.utils.module_loading import (autodiscover_modules, import_by_path, import_string,
+ module_has_submodule)
from django.utils._os import upath
@@ -107,15 +109,13 @@ class EggLoader(unittest.TestCase):
self.assertRaises(ImportError, import_module, 'egg_module.sub1.sub2.no_such_module')
-class ModuleImportTestCase(unittest.TestCase):
+class ModuleImportTestCase(IgnorePendingDeprecationWarningsMixin, unittest.TestCase):
def test_import_by_path(self):
- cls = import_by_path(
- 'django.utils.module_loading.import_by_path')
+ 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',
- 'utils_tests.unexistent'):
+ for path in ('no_dots_in_path', 'unexistent.path', 'utils_tests.unexistent'):
self.assertRaises(ImproperlyConfigured, import_by_path, path)
with self.assertRaises(ImproperlyConfigured) as cm:
@@ -132,6 +132,24 @@ class ModuleImportTestCase(unittest.TestCase):
self.assertIsNotNone(traceback.tb_next.tb_next,
'Should have more than the calling frame in the traceback.')
+ def test_import_by_path_pending_deprecation_warning(self):
+ with warnings.catch_warnings(record=True) as w:
+ warnings.simplefilter('always', category=PendingDeprecationWarning)
+ cls = import_by_path('django.utils.module_loading.import_by_path')
+ self.assertEqual(cls, import_by_path)
+ self.assertEqual(len(w), 1)
+ self.assertTrue(issubclass(w[-1].category, PendingDeprecationWarning))
+ self.assertIn('deprecated', str(w[-1].message))
+
+ def test_import_string(self):
+ cls = import_string('django.utils.module_loading.import_string')
+ self.assertEqual(cls, import_string)
+
+ # Test exceptions raised
+ self.assertRaises(ImportError, import_string, 'no_dots_in_path')
+ self.assertRaises(ImportError, import_string, 'utils_tests.unexistent')
+ self.assertRaises(ImportError, import_string, 'unexistent.path')
+
@modify_settings(INSTALLED_APPS={'append': 'utils_tests.test_module'})
class AutodiscoverModulesTestCase(SimpleTestCase):
diff --git a/tests/wsgi/tests.py b/tests/wsgi/tests.py
index 567ca94f98..299beacf46 100644
--- a/tests/wsgi/tests.py
+++ b/tests/wsgi/tests.py
@@ -101,6 +101,6 @@ class GetInternalWSGIApplicationTest(unittest.TestCase):
def test_bad_name(self):
with six.assertRaisesRegex(self,
ImproperlyConfigured,
- r"^WSGI application 'wsgi.wsgi.noexist' could not be loaded; Module.*"):
+ r"^WSGI application 'wsgi.wsgi.noexist' could not be loaded; Error importing.*"):
get_internal_wsgi_application()