Commit b161c01c authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

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.
parent d3543af7
Loading
Loading
Loading
Loading
+12 −7
Original line number Diff line number Diff line
@@ -87,6 +87,10 @@ class AppConfig(object):
            module = import_module(entry)

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

            # Raise the original exception when entry cannot be a path to an
@@ -104,8 +108,8 @@ class AppConfig(object):
            else:
                mod_path, _, cls_name = entry.rpartition('.')

        # If we're reaching this point, we must load the app config class
        # located at <mod_path>.<cls_name>.
        # If we're reaching this point, we must attempt to load the app config
        # class located at <mod_path>.<cls_name>

        # Avoid django.utils.module_loading.import_by_path because it
        # masks errors -- it reraises ImportError as ImproperlyConfigured.
@@ -113,11 +117,12 @@ class AppConfig(object):
        try:
            cls = getattr(mod, cls_name)
        except AttributeError:
            # Emulate the error that "from <mod_path> import <cls_name>"
            # would raise when <mod_path> exists but not <cls_name>, with
            # more context (Python just says "cannot import name ...").
            raise ImportError(
                "cannot import name '%s' from '%s'" % (cls_name, mod_path))
            if module is None:
                # If importing as an app module failed, that error probably
                # contains the most informative traceback. Trigger it again.
                import_module(entry)
            else:
                raise

        # Check for obvious errors. (This check prevents duck typing, but
        # it could be removed if it became a problem in practice.)
+1 −0
Original line number Diff line number Diff line
raise ImportError("Oops")
+8 −0
Original line number Diff line number Diff line
@@ -166,6 +166,14 @@ class AppsTests(TestCase):
            with self.settings(INSTALLED_APPS=['apps.apps.RelabeledAppsConfig', 'apps']):
                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):
        """
        Tests that the models in the models.py file were loaded correctly.