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>
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

View File

@ -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)))

View File

@ -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():

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.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)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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

View File

@ -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):

View File

@ -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)

View File

@ -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:

View File

@ -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)

View File

@ -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):

View File

@ -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)

View File

@ -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__(

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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_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=''):
"""
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:
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)
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

View File

@ -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)

View File

@ -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

View File

@ -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``
===========================

View File

@ -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``
~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -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')

View File

@ -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):

View File

@ -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):

View File

@ -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()