Kaydet (Commit) b161c01c authored tarafından Aymeric Augustin's avatar Aymeric Augustin

Fixed #22920 -- Avoid masking some exceptions.

If loading an application trigger an ImportError, the details of that
error were lost in some cases. Thanks Ben Davis for the report.
üst d3543af7
...@@ -87,6 +87,10 @@ class AppConfig(object): ...@@ -87,6 +87,10 @@ class AppConfig(object):
module = import_module(entry) module = import_module(entry)
except ImportError: except ImportError:
# Track that importing as an app module failed. If importing as an
# app config class fails too, we'll trigger the ImportError again.
module = None
mod_path, _, cls_name = entry.rpartition('.') mod_path, _, cls_name = entry.rpartition('.')
# Raise the original exception when entry cannot be a path to an # Raise the original exception when entry cannot be a path to an
...@@ -104,8 +108,8 @@ class AppConfig(object): ...@@ -104,8 +108,8 @@ class AppConfig(object):
else: else:
mod_path, _, cls_name = entry.rpartition('.') mod_path, _, cls_name = entry.rpartition('.')
# If we're reaching this point, we must load the app config class # If we're reaching this point, we must attempt to load the app config
# located at <mod_path>.<cls_name>. # class located at <mod_path>.<cls_name>
# Avoid django.utils.module_loading.import_by_path because it # Avoid django.utils.module_loading.import_by_path because it
# masks errors -- it reraises ImportError as ImproperlyConfigured. # masks errors -- it reraises ImportError as ImproperlyConfigured.
...@@ -113,11 +117,12 @@ class AppConfig(object): ...@@ -113,11 +117,12 @@ class AppConfig(object):
try: try:
cls = getattr(mod, cls_name) cls = getattr(mod, cls_name)
except AttributeError: except AttributeError:
# Emulate the error that "from <mod_path> import <cls_name>" if module is None:
# would raise when <mod_path> exists but not <cls_name>, with # If importing as an app module failed, that error probably
# more context (Python just says "cannot import name ..."). # contains the most informative traceback. Trigger it again.
raise ImportError( import_module(entry)
"cannot import name '%s' from '%s'" % (cls_name, mod_path)) else:
raise
# Check for obvious errors. (This check prevents duck typing, but # Check for obvious errors. (This check prevents duck typing, but
# it could be removed if it became a problem in practice.) # it could be removed if it became a problem in practice.)
......
...@@ -166,6 +166,14 @@ class AppsTests(TestCase): ...@@ -166,6 +166,14 @@ class AppsTests(TestCase):
with self.settings(INSTALLED_APPS=['apps.apps.RelabeledAppsConfig', 'apps']): with self.settings(INSTALLED_APPS=['apps.apps.RelabeledAppsConfig', 'apps']):
pass pass
def test_import_exception_is_not_masked(self):
"""
App discovery should preserve stack traces. Regression test for #22920.
"""
with six.assertRaisesRegex(self, ImportError, "Oops"):
with self.settings(INSTALLED_APPS=['apps.failing_app']):
pass
def test_models_py(self): def test_models_py(self):
""" """
Tests that the models in the models.py file were loaded correctly. Tests that the models in the models.py file were loaded correctly.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment