Fixed #21674 -- Deprecated the import_by_path() function in favor of import_string().

Thanks Aymeric Augustin for the suggestion and review.
This commit is contained in:
Berker Peksag 2014-01-20 22:15:14 +02:00 committed by Tim Graham
parent fcc21837dc
commit 5d263dee30
31 changed files with 155 additions and 95 deletions

View File

@ -484,6 +484,7 @@ answer newbie questions, and generally made Django that much better:
John Paulett <john@paulett.org> John Paulett <john@paulett.org>
pavithran s <pavithran.s@gmail.com> pavithran s <pavithran.s@gmail.com>
Barry Pederson <bp@barryp.org> Barry Pederson <bp@barryp.org>
Berker Peksag <berker.peksag@gmail.com>
Andreas Pelme <andreas@pelme.se> Andreas Pelme <andreas@pelme.se>
permonik@mesias.brnonet.cz permonik@mesias.brnonet.cz
peter@mymart.com peter@mymart.com

View File

@ -2,7 +2,7 @@ import os
from unittest import SkipTest from unittest import SkipTest
from django.contrib.staticfiles.testing import StaticLiveServerCase 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 _ from django.utils.translation import ugettext as _
@ -22,7 +22,7 @@ class AdminSeleniumWebDriverTestCase(StaticLiveServerCase):
if not os.environ.get('DJANGO_SELENIUM_TESTS', False): if not os.environ.get('DJANGO_SELENIUM_TESTS', False):
raise SkipTest('Selenium tests not requested') raise SkipTest('Selenium tests not requested')
try: try:
cls.selenium = import_by_path(cls.webdriver_class)() cls.selenium = import_string(cls.webdriver_class)()
except Exception as e: except Exception as e:
raise SkipTest('Selenium webdriver "%s" not installed or not ' raise SkipTest('Selenium webdriver "%s" not installed or not '
'operational: %s' % (cls.webdriver_class, str(e))) 'operational: %s' % (cls.webdriver_class, str(e)))

View File

@ -4,7 +4,7 @@ import re
from django.apps import apps as django_apps from django.apps import apps as django_apps
from django.conf import settings from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, PermissionDenied 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 django.middleware.csrf import rotate_token
from .signals import user_logged_in, user_logged_out, user_login_failed from .signals import user_logged_in, user_logged_out, user_login_failed
@ -15,7 +15,7 @@ REDIRECT_FIELD_NAME = 'next'
def load_backend(path): def load_backend(path):
return import_by_path(path)() return import_string(path)()
def get_backends(): def get_backends():

View File

@ -13,7 +13,7 @@ from django.utils.encoding import force_bytes, force_str, force_text
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.crypto import ( from django.utils.crypto import (
pbkdf2, constant_time_compare, get_random_string) 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 _ from django.utils.translation import ugettext_noop as _
@ -92,7 +92,7 @@ def load_hashers(password_hashers=None):
if not password_hashers: if not password_hashers:
password_hashers = settings.PASSWORD_HASHERS password_hashers = settings.PASSWORD_HASHERS
for backend in password_hashers: for backend in password_hashers:
hasher = import_by_path(backend)() hasher = import_string(backend)()
if not getattr(hasher, 'algorithm'): if not getattr(hasher, 'algorithm'):
raise ImproperlyConfigured("hasher doesn't specify an " raise ImproperlyConfigured("hasher doesn't specify an "
"algorithm name: %s" % backend) "algorithm name: %s" % backend)

View File

@ -58,7 +58,7 @@ class RemoteUserMiddleware(object):
auth.BACKEND_SESSION_KEY, '')) auth.BACKEND_SESSION_KEY, ''))
if isinstance(stored_backend, RemoteUserBackend): if isinstance(stored_backend, RemoteUserBackend):
auth.logout(request) auth.logout(request)
except ImproperlyConfigured: except ImportError:
# backend failed to load # backend failed to load
auth.logout(request) auth.logout(request)
return return

View File

@ -1,5 +1,4 @@
from django.core.exceptions import ImproperlyConfigured from django.utils.module_loading import import_string
from django.utils.module_loading import import_by_path
from django.contrib.formtools.wizard.storage.base import BaseStorage from django.contrib.formtools.wizard.storage.base import BaseStorage
from django.contrib.formtools.wizard.storage.exceptions import ( from django.contrib.formtools.wizard.storage.exceptions import (
@ -12,7 +11,7 @@ __all__ = [
def get_storage(path, *args, **kwargs): def get_storage(path, *args, **kwargs):
try: try:
storage_class = import_by_path(path) storage_class = import_string(path)
except ImproperlyConfigured as e: except ImportError as e:
raise MissingStorage('Error loading storage: %s' % e) raise MissingStorage('Error loading storage: %s' % e)
return storage_class(*args, **kwargs) return storage_class(*args, **kwargs)

View File

@ -1,12 +1,12 @@
from django.conf import settings 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): def default_storage(request):
""" """
Callable with the same interface as the storage classes. 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. to avoid accessing the settings at the module level.
""" """
return import_by_path(settings.MESSAGE_STORAGE)(request) return import_string(settings.MESSAGE_STORAGE)(request)

View File

@ -12,7 +12,7 @@ from django.utils.crypto import get_random_string
from django.utils.crypto import salted_hmac from django.utils.crypto import salted_hmac
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_bytes, force_text 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 from django.contrib.sessions.exceptions import SuspiciousSession
@ -40,7 +40,7 @@ class SessionBase(object):
self._session_key = session_key self._session_key = session_key
self.accessed = False self.accessed = False
self.modified = False self.modified = False
self.serializer = import_by_path(settings.SESSION_SERIALIZER) self.serializer = import_string(settings.SESSION_SERIALIZER)
def __contains__(self, key): def __contains__(self, key):
return key in self._session return key in self._session

View File

@ -6,7 +6,7 @@ from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.files.storage import default_storage, Storage, FileSystemStorage from django.core.files.storage import default_storage, Storage, FileSystemStorage
from django.utils.functional import empty, LazyObject 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._os import safe_join
from django.utils import six, lru_cache 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 Imports the staticfiles finder class described by import_path, where
import_path is the full Python path to the class. 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): if not issubclass(Finder, BaseFinder):
raise ImproperlyConfigured('Finder "%s" is not a subclass of "%s"' % raise ImproperlyConfigured('Finder "%s" is not a subclass of "%s"' %
(Finder, BaseFinder)) (Finder, BaseFinder))

View File

@ -20,7 +20,7 @@ from django.core import signals
from django.core.cache.backends.base import ( from django.core.cache.backends.base import (
InvalidCacheBackendError, CacheKeyWarning, BaseCache) InvalidCacheBackendError, CacheKeyWarning, BaseCache)
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.module_loading import import_by_path from django.utils.module_loading import import_string
__all__ = [ __all__ = [
@ -69,8 +69,8 @@ def _create_cache(backend, **kwargs):
except KeyError: except KeyError:
try: try:
# Trying to import the given backend, in case it's a dotted path # Trying to import the given backend, in case it's a dotted path
import_by_path(backend) import_string(backend)
except ImproperlyConfigured as e: except ImportError as e:
raise InvalidCacheBackendError("Could not find backend '%s': %s" % ( raise InvalidCacheBackendError("Could not find backend '%s': %s" % (
backend, e)) backend, e))
location = kwargs.pop('LOCATION', '') location = kwargs.pop('LOCATION', '')
@ -80,8 +80,8 @@ def _create_cache(backend, **kwargs):
params.update(kwargs) params.update(kwargs)
backend = params.pop('BACKEND') backend = params.pop('BACKEND')
location = params.pop('LOCATION', '') location = params.pop('LOCATION', '')
backend_cls = import_by_path(backend) backend_cls = import_string(backend)
except (AttributeError, ImportError, ImproperlyConfigured) as e: except ImportError as e:
raise InvalidCacheBackendError( raise InvalidCacheBackendError(
"Could not find backend '%s': %s" % (backend, e)) "Could not find backend '%s': %s" % (backend, e))
return backend_cls(location, params) return backend_cls(location, params)

View File

@ -5,7 +5,7 @@ import time
import warnings import warnings
from django.core.exceptions import ImproperlyConfigured, DjangoRuntimeWarning 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): class InvalidCacheBackendError(ImproperlyConfigured):
@ -45,7 +45,7 @@ def get_key_func(key_func):
if callable(key_func): if callable(key_func):
return key_func return key_func
else: else:
return import_by_path(key_func) return import_string(key_func)
return default_key_func return default_key_func

View File

@ -9,7 +9,7 @@ from django.core.files import locks, File
from django.core.files.move import file_move_safe from django.core.files.move import file_move_safe
from django.utils.encoding import force_text, filepath_to_uri from django.utils.encoding import force_text, filepath_to_uri
from django.utils.functional import LazyObject 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.six.moves.urllib.parse import urljoin
from django.utils.text import get_valid_filename from django.utils.text import get_valid_filename
from django.utils._os import safe_join, abspathu from django.utils._os import safe_join, abspathu
@ -301,7 +301,7 @@ class FileSystemStorage(Storage):
def get_storage_class(import_path=None): 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): class DefaultStorage(LazyObject):

View File

@ -9,7 +9,7 @@ from io import BytesIO
from django.conf import settings from django.conf import settings
from django.core.files.uploadedfile import TemporaryUploadedFile, InMemoryUploadedFile from django.core.files.uploadedfile import TemporaryUploadedFile, InMemoryUploadedFile
from django.utils.encoding import python_2_unicode_compatible 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__ = [ __all__ = [
'UploadFileException', 'StopUpload', 'SkipFile', 'FileUploadHandler', 'UploadFileException', 'StopUpload', 'SkipFile', 'FileUploadHandler',
@ -214,4 +214,4 @@ def load_handler(path, *args, **kwargs):
<TemporaryFileUploadHandler object at 0x...> <TemporaryFileUploadHandler object at 0x...>
""" """
return import_by_path(path)(*args, **kwargs) return import_string(path)(*args, **kwargs)

View File

@ -11,7 +11,7 @@ from django.core import signals
from django.core.exceptions import MiddlewareNotUsed, PermissionDenied, SuspiciousOperation from django.core.exceptions import MiddlewareNotUsed, PermissionDenied, SuspiciousOperation
from django.db import connections, transaction from django.db import connections, transaction
from django.utils.encoding import force_text 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.utils import six
from django.views import debug from django.views import debug
@ -43,7 +43,7 @@ class BaseHandler(object):
request_middleware = [] request_middleware = []
for middleware_path in settings.MIDDLEWARE_CLASSES: for middleware_path in settings.MIDDLEWARE_CLASSES:
mw_class = import_by_path(middleware_path) mw_class = import_string(middleware_path)
try: try:
mw_instance = mw_class() mw_instance = mw_class()
except MiddlewareNotUsed: except MiddlewareNotUsed:

View File

@ -4,7 +4,7 @@ Tools for sending email.
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf import settings 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 # Imported for backwards compatibility, and for the sake
# of a cleaner namespace. These symbols used to be in # 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 Both fail_silently and other keyword arguments are used in the
constructor of the backend. 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) return klass(fail_silently=fail_silently, **kwds)

View File

@ -16,9 +16,11 @@ import traceback
from wsgiref import simple_server from wsgiref import simple_server
from wsgiref.util import FileWrapper # NOQA: for backwards compatibility 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.management.color import color_style
from django.core.wsgi import get_wsgi_application 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 from django.utils.six.moves import socketserver
__all__ = ('WSGIServer', 'WSGIRequestHandler', 'MAX_SOCKET_CHUNK_SIZE') __all__ = ('WSGIServer', 'WSGIRequestHandler', 'MAX_SOCKET_CHUNK_SIZE')
@ -50,10 +52,18 @@ def get_internal_wsgi_application():
if app_path is None: if app_path is None:
return get_wsgi_application() return get_wsgi_application()
return import_by_path( try:
app_path, return import_string(app_path)
error_prefix="WSGI application '%s' could not be loaded; " % 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): class ServerHandler(simple_server.ServerHandler, object):

View File

@ -44,7 +44,7 @@ from django.conf import settings
from django.utils import baseconv from django.utils import baseconv
from django.utils.crypto import constant_time_compare, salted_hmac from django.utils.crypto import constant_time_compare, salted_hmac
from django.utils.encoding import force_bytes, force_str, force_text 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): class BadSignature(Exception):
@ -75,7 +75,7 @@ def base64_hmac(salt, value, key):
def get_cookie_signer(salt='django.core.signing.get_cookie_signer'): 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) return Signer('django.http.cookies' + settings.SECRET_KEY, salt=salt)

View File

@ -3,7 +3,7 @@ from django.apps.registry import Apps
from django.db import models from django.db import models
from django.db.models.options import DEFAULT_NAMES, normalize_unique_together from django.db.models.options import DEFAULT_NAMES, normalize_unique_together
from django.utils import six 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): class InvalidBasesError(ValueError):
@ -115,7 +115,7 @@ class ModelState(object):
fields = [] fields = []
for field in model._meta.local_fields: for field in model._meta.local_fields:
name, path, args, kwargs = field.deconstruct() name, path, args, kwargs = field.deconstruct()
field_class = import_by_path(path) field_class = import_string(path)
try: try:
fields.append((name, field_class(*args, **kwargs))) fields.append((name, field_class(*args, **kwargs)))
except TypeError as e: except TypeError as e:
@ -127,7 +127,7 @@ class ModelState(object):
)) ))
for field in model._meta.local_many_to_many: for field in model._meta.local_many_to_many:
name, path, args, kwargs = field.deconstruct() name, path, args, kwargs = field.deconstruct()
field_class = import_by_path(path) field_class = import_string(path)
try: try:
fields.append((name, field_class(*args, **kwargs))) fields.append((name, field_class(*args, **kwargs)))
except TypeError as e: except TypeError as e:
@ -175,7 +175,7 @@ class ModelState(object):
fields = [] fields = []
for name, field in self.fields: for name, field in self.fields:
_, path, args, kwargs = field.deconstruct() _, path, args, kwargs = field.deconstruct()
field_class = import_by_path(path) field_class = import_string(path)
fields.append((name, field_class(*args, **kwargs))) fields.append((name, field_class(*args, **kwargs)))
# Now make a copy # Now make a copy
return self.__class__( return self.__class__(

View File

@ -7,7 +7,7 @@ import warnings
from django.conf import settings from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.functional import cached_property 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._os import upath
from django.utils import six from django.utils import six
@ -221,7 +221,7 @@ class ConnectionRouter(object):
routers = [] routers = []
for r in self._routers: for r in self._routers:
if isinstance(r, six.string_types): if isinstance(r, six.string_types):
router = import_by_path(r)() router = import_string(r)()
else: else:
router = r router = r
routers.append(router) routers.append(router)

View File

@ -1,5 +1,5 @@
from copy import copy 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. # Cache of actual callables.
_standard_context_processors = None _standard_context_processors = None
@ -162,7 +162,7 @@ def get_standard_processors():
collect.extend(_builtin_context_processors) collect.extend(_builtin_context_processors)
collect.extend(settings.TEMPLATE_CONTEXT_PROCESSORS) collect.extend(settings.TEMPLATE_CONTEXT_PROCESSORS)
for path in collect: for path in collect:
func = import_by_path(path) func = import_string(path)
processors.append(func) processors.append(func)
_standard_context_processors = tuple(processors) _standard_context_processors = tuple(processors)
return _standard_context_processors return _standard_context_processors

View File

@ -28,7 +28,7 @@
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.template.base import Origin, Template, Context, TemplateDoesNotExist from django.template.base import Origin, Template, Context, TemplateDoesNotExist
from django.conf import settings 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 from django.utils import six
template_source_loaders = None template_source_loaders = None
@ -95,7 +95,7 @@ def find_template_loader(loader):
else: else:
args = [] args = []
if isinstance(loader, six.string_types): if isinstance(loader, six.string_types):
TemplateLoader = import_by_path(loader) TemplateLoader = import_string(loader)
if hasattr(TemplateLoader, 'load_template_source'): if hasattr(TemplateLoader, 'load_template_source'):
func = TemplateLoader(*args) func = TemplateLoader(*args)

View File

@ -5,7 +5,7 @@ import warnings
from django.conf import settings from django.conf import settings
from django.core import mail from django.core import mail
from django.core.mail import get_connection 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 from django.views.debug import ExceptionReporter, get_exception_reporter_filter
# Imports kept for backwards-compatibility in Django 1.7. # Imports kept for backwards-compatibility in Django 1.7.
@ -73,7 +73,7 @@ def configure_logging(logging_config, logging_settings):
if logging_config: if logging_config:
# First find the logging configuration function ... # 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) logging_config_func(DEFAULT_LOGGING)

View File

@ -5,33 +5,48 @@ import imp
from importlib import import_module from importlib import import_module
import os import os
import sys import sys
import warnings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils import six from django.utils import six
def import_string(dotted_path):
"""
Import a dotted module path and return the attribute/class designated by the
last name in the path. Raise ImportError if the import failed.
"""
try:
module_path, class_name = dotted_path.rsplit('.', 1)
except ValueError:
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:
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=''): def import_by_path(dotted_path, error_prefix=''):
""" """
Import a dotted module path and return the attribute/class designated by the 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 ImproperlyConfigured if something goes wrong.
""" """
warnings.warn(
'import_by_path() has been deprecated. Use import_string() instead.',
PendingDeprecationWarning, stacklevel=2)
try: try:
module_path, class_name = dotted_path.rsplit('.', 1) attr = import_string(dotted_path)
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: except ImportError as e:
msg = '%sError importing module %s: "%s"' % ( msg = '%sError importing module %s: "%s"' % (
error_prefix, module_path, e) error_prefix, dotted_path, e)
six.reraise(ImproperlyConfigured, ImproperlyConfigured(msg), six.reraise(ImproperlyConfigured, ImproperlyConfigured(msg),
sys.exc_info()[2]) 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 return attr

View File

@ -14,7 +14,7 @@ from django.template.defaultfilters import force_escape, pprint
from django.utils.datastructures import MultiValueDict from django.utils.datastructures import MultiValueDict
from django.utils.html import escape from django.utils.html import escape
from django.utils.encoding import force_bytes, smart_text 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 from django.utils import six
HIDDEN_SETTINGS = re.compile('API|TOKEN|KEY|SECRET|PASS|PROFANITIES_LIST|SIGNATURE') 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 global default_exception_reporter_filter
if default_exception_reporter_filter is None: if default_exception_reporter_filter is None:
# Load the default filter for the first time and cache it. # 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)() settings.DEFAULT_EXCEPTION_REPORTER_FILTER)()
if request: if request:
return getattr(request, 'exception_reporter_filter', default_exception_reporter_filter) return getattr(request, 'exception_reporter_filter', default_exception_reporter_filter)

View File

@ -116,6 +116,9 @@ details on these changes.
* ``django.db.backends.DatabaseValidation.validate_field`` will be removed in * ``django.db.backends.DatabaseValidation.validate_field`` will be removed in
favor of the ``check_field`` method. 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: .. _deprecation-removed-in-1.8:
1.8 1.8

View File

@ -709,22 +709,31 @@ escaping HTML.
Functions for working with Python modules. 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 Imports a dotted module path and returns the attribute/class designated by
the last name in the path. Raises the last name in the path. Raises ``ImportError`` if the import failed. For
:exc:`~django.core.exceptions.ImproperlyConfigured` if something goes example::
wrong. For example::
from django.utils.module_loading import import_by_path from django.utils.module_loading import import_string
ImproperlyConfigured = import_by_path('django.core.exceptions.ImproperlyConfigured') ImproperlyConfigured = import_string('django.core.exceptions.ImproperlyConfigured')
is equivalent to:: is equivalent to::
from django.core.exceptions import ImproperlyConfigured 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`` ``django.utils.safestring``
=========================== ===========================

View File

@ -1042,6 +1042,17 @@ Features deprecated in 1.7
respectively :mod:`logging.config` and :mod:`importlib` provided for Python respectively :mod:`logging.config` and :mod:`importlib` provided for Python
versions prior to 2.7. They have been deprecated. 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`` ``django.utils.tzinfo``
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -16,7 +16,7 @@ except ImportError:
import dummy_threading as threading import dummy_threading as threading
from django.core.cache import cache 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.base import File, ContentFile
from django.core.files.storage import FileSystemStorage, get_storage_class from django.core.files.storage import FileSystemStorage, get_storage_class
from django.core.files.uploadedfile import SimpleUploadedFile 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. get_storage_class raises an error if the requested import don't exist.
""" """
with six.assertRaisesRegex(self, ImproperlyConfigured, with six.assertRaisesRegex(self, ImportError, "No module named '?storage'?"):
"Error importing module storage: \"No module named '?storage'?\""):
get_storage_class('storage.NonExistingStorage') get_storage_class('storage.NonExistingStorage')
def test_get_nonexisting_storage_class(self): def test_get_nonexisting_storage_class(self):
""" """
get_storage_class raises an error if the requested class don't exist. get_storage_class raises an error if the requested class don't exist.
""" """
self.assertRaisesMessage( self.assertRaises(ImportError, get_storage_class,
ImproperlyConfigured, 'django.core.files.storage.NonExistingStorage')
'Module "django.core.files.storage" does not define a '
'"NonExistingStorage" attribute/class',
get_storage_class,
'django.core.files.storage.NonExistingStorage')
def test_get_nonexisting_storage_module(self): def test_get_nonexisting_storage_module(self):
""" """
get_storage_class raises an error if the requested module don't exist. get_storage_class raises an error if the requested module don't exist.
""" """
# Error message may or may not be the fully qualified path. # Error message may or may not be the fully qualified path.
with six.assertRaisesRegex(self, ImproperlyConfigured, with six.assertRaisesRegex(self, ImportError,
"Error importing module django.core.files.non_existing_storage: " "No module named '?(django.core.files.)?non_existing_storage'?"):
"\"No module named '?(django.core.files.)?non_existing_storage'?\""):
get_storage_class( get_storage_class(
'django.core.files.non_existing_storage.NonExistingStorage') 'django.core.files.non_existing_storage.NonExistingStorage')

View File

@ -788,11 +788,11 @@ class TestMiscFinder(TestCase):
finders.FileSystemFinder) finders.FileSystemFinder)
def test_get_finder_bad_classname(self): def test_get_finder_bad_classname(self):
self.assertRaises(ImproperlyConfigured, finders.get_finder, self.assertRaises(ImportError, finders.get_finder,
'django.contrib.staticfiles.finders.FooBarFinder') 'django.contrib.staticfiles.finders.FooBarFinder')
def test_get_finder_bad_module(self): def test_get_finder_bad_module(self):
self.assertRaises(ImproperlyConfigured, self.assertRaises(ImportError,
finders.get_finder, 'foo.bar.FooBarFinder') finders.get_finder, 'foo.bar.FooBarFinder')
def test_cache(self): def test_cache(self):

View File

@ -3,13 +3,15 @@ from importlib import import_module
import os import os
import sys import sys
import unittest import unittest
import warnings
from zipimport import zipimporter from zipimport import zipimporter
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.test import SimpleTestCase, modify_settings 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 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 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') 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): def test_import_by_path(self):
cls = import_by_path( cls = import_by_path('django.utils.module_loading.import_by_path')
'django.utils.module_loading.import_by_path')
self.assertEqual(cls, import_by_path) self.assertEqual(cls, import_by_path)
# Test exceptions raised # Test exceptions raised
for path in ('no_dots_in_path', 'unexistent.path', for path in ('no_dots_in_path', 'unexistent.path', 'utils_tests.unexistent'):
'utils_tests.unexistent'):
self.assertRaises(ImproperlyConfigured, import_by_path, path) self.assertRaises(ImproperlyConfigured, import_by_path, path)
with self.assertRaises(ImproperlyConfigured) as cm: with self.assertRaises(ImproperlyConfigured) as cm:
@ -132,6 +132,24 @@ class ModuleImportTestCase(unittest.TestCase):
self.assertIsNotNone(traceback.tb_next.tb_next, self.assertIsNotNone(traceback.tb_next.tb_next,
'Should have more than the calling frame in the traceback.') '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'}) @modify_settings(INSTALLED_APPS={'append': 'utils_tests.test_module'})
class AutodiscoverModulesTestCase(SimpleTestCase): class AutodiscoverModulesTestCase(SimpleTestCase):

View File

@ -101,6 +101,6 @@ class GetInternalWSGIApplicationTest(unittest.TestCase):
def test_bad_name(self): def test_bad_name(self):
with six.assertRaisesRegex(self, with six.assertRaisesRegex(self,
ImproperlyConfigured, 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() get_internal_wsgi_application()