diff --git a/django/conf/__init__.py b/django/conf/__init__.py index 5568d7cc83..b0a448ca1e 100644 --- a/django/conf/__init__.py +++ b/django/conf/__init__.py @@ -163,7 +163,13 @@ class Settings: # store the settings module in case someone later cares self.SETTINGS_MODULE = settings_module - mod = importlib.import_module(self.SETTINGS_MODULE) + try: + mod = importlib.import_module(self.SETTINGS_MODULE) + except ImportError as exc: + # If the settings module cannot be imported, treat it as a configuration error. + if exc.name == self.SETTINGS_MODULE: + raise ImproperlyConfigured(f"Settings module {self.SETTINGS_MODULE} could not be imported") from exc + raise tuple_settings = ( "ALLOWED_HOSTS", diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py index 0c16447d58..5e104e098d 100644 --- a/django/core/management/__init__.py +++ b/django/core/management/__init__.py @@ -382,7 +382,9 @@ class ManagementUtility: settings.INSTALLED_APPS except ImproperlyConfigured as exc: self.settings_exception = exc - except ImportError as exc: + # The following commands can be run even without a valid settings file configured + if subcommand not in {'startproject', 'startapp', 'makemessages'}: + sys.stderr.write(str(exc) + '\n') self.settings_exception = exc if settings.configured: diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py index 5d8a5ec97e..e5dde57bad 100644 --- a/tests/admin_scripts/tests.py +++ b/tests/admin_scripts/tests.py @@ -1412,12 +1412,12 @@ class ManageSettingsWithSettingsErrors(AdminScriptTestCase): self.write_settings( "settings.py", extra="from django.core.exceptions import ImproperlyConfigured\n" - "raise ImproperlyConfigured()", + "raise ImproperlyConfigured('Improper configuration')", ) args = ["help"] out, err = self.run_manage(args) self.assertOutput(out, "only Django core commands are listed") - self.assertNoOutput(err) + self.assertOutput(err, "Improper configuration") class ManageCheck(AdminScriptTestCase): diff --git a/tests/settings_tests/tests.py b/tests/settings_tests/tests.py index 4fc35689d6..f1b59359b3 100644 --- a/tests/settings_tests/tests.py +++ b/tests/settings_tests/tests.py @@ -338,6 +338,20 @@ class SettingsTests(SimpleTestCase): with self.assertRaisesMessage(ValueError, "Incorrect timezone setting: test"): settings._setup() + def test_unable_to_import_settings_module(self): + with self.assertRaisesMessage( + ImproperlyConfigured, "Settings module fake_settings_module could not be imported" + ): + Settings('fake_settings_module') + + def test_unable_to_import_a_random_module(self): + def mock_import_module(_): + raise ModuleNotFoundError("No module named 'fake_module'", name="fake_module") + + with mock.patch("importlib.import_module", mock_import_module): + with self.assertRaisesMessage(ImportError, "No module named 'fake_module'"): + Settings('fake_settings_module') + class TestComplexSettingOverride(SimpleTestCase): def setUp(self):