1
0
mirror of https://github.com/django/django.git synced 2025-04-13 03:52:20 +00:00

Fixed #36224 -- Fixed shell imports when settings not configured.

Thank you Raffaella for the report. Thank you Tim Schilling and Natalia Bidart
for the reviews.
This commit is contained in:
Sarah Boyce 2025-03-03 17:19:24 +01:00
parent 647dca4132
commit de1117ea8e
2 changed files with 93 additions and 15 deletions

View File

@ -6,6 +6,7 @@ from collections import defaultdict
from importlib import import_module
from django.apps import apps
from django.core.exceptions import AppRegistryNotReady
from django.core.management import BaseCommand, CommandError
from django.utils.datastructures import OrderedSet
from django.utils.module_loading import import_string as import_dotted_path
@ -150,6 +151,22 @@ class Command(BaseCommand):
if options and options.get("no_imports"):
return {}
verbosity = options["verbosity"] if options else 0
try:
apps.check_models_ready()
except AppRegistryNotReady:
if verbosity > 0:
settings_env_var = os.getenv("DJANGO_SETTINGS_MODULE")
self.stdout.write(
"Automatic imports are disabled since settings are not configured."
f"\nDJANGO_SETTINGS_MODULE value is {settings_env_var!r}.\n"
"HINT: Ensure that the settings module is configured and set.",
self.style.ERROR,
ending="\n\n",
)
return {}
path_imports = self.get_auto_imports()
if path_imports is None:
return {}
@ -175,7 +192,6 @@ class Command(BaseCommand):
name: obj for items in auto_imports.values() for name, obj in items
}
verbosity = options["verbosity"] if options else 0
if verbosity < 1:
return namespace
@ -228,7 +244,7 @@ class Command(BaseCommand):
def handle(self, **options):
# Execute the command and exit.
if options["command"]:
exec(options["command"], {**globals(), **self.get_namespace()})
exec(options["command"], {**globals(), **self.get_namespace(**options)})
return
# Execute stdin if it has anything to read and exit.
@ -238,7 +254,7 @@ class Command(BaseCommand):
and not sys.stdin.isatty()
and select.select([sys.stdin], [], [], 0)[0]
):
exec(sys.stdin.read(), {**globals(), **self.get_namespace()})
exec(sys.stdin.read(), {**globals(), **self.get_namespace(**options)})
return
available_shells = (

View File

@ -1,3 +1,5 @@
import os
import subprocess
import sys
import unittest
from unittest import mock
@ -23,25 +25,85 @@ class ShellCommandTestCase(SimpleTestCase):
def test_command_option(self):
with self.assertLogs("test", "INFO") as cm:
call_command(
"shell",
command=(
"import django; from logging import getLogger; "
'getLogger("test").info(django.__version__)'
),
)
with captured_stdout():
call_command(
"shell",
command=(
"import django; from logging import getLogger; "
'getLogger("test").info(django.__version__)'
),
)
self.assertEqual(cm.records[0].getMessage(), __version__)
def test_command_option_globals(self):
with captured_stdout() as stdout:
call_command("shell", command=self.script_globals)
call_command("shell", command=self.script_globals, verbosity=0)
self.assertEqual(stdout.getvalue().strip(), "True")
def test_command_option_inline_function_call(self):
with captured_stdout() as stdout:
call_command("shell", command=self.script_with_inline_function)
call_command("shell", command=self.script_with_inline_function, verbosity=0)
self.assertEqual(stdout.getvalue().strip(), __version__)
@override_settings(INSTALLED_APPS=["shell"])
def test_no_settings(self):
test_environ = os.environ.copy()
if "DJANGO_SETTINGS_MODULE" in test_environ:
del test_environ["DJANGO_SETTINGS_MODULE"]
error = (
"Automatic imports are disabled since settings are not configured.\n"
"DJANGO_SETTINGS_MODULE value is None.\n"
"HINT: Ensure that the settings module is configured and set.\n\n"
)
for verbosity, assertError in [
("0", self.assertNotIn),
("1", self.assertIn),
("2", self.assertIn),
]:
with self.subTest(verbosity=verbosity, get_auto_imports="models"):
p = subprocess.run(
[
sys.executable,
"-m",
"django",
"shell",
"-c",
"print(globals())",
"-v",
verbosity,
],
capture_output=True,
env=test_environ,
text=True,
umask=-1,
)
assertError(error, p.stdout)
self.assertNotIn("Marker", p.stdout)
with self.subTest(verbosity=verbosity, get_auto_imports="without-models"):
with mock.patch(
"django.core.management.commands.shell.Command.get_auto_imports",
return_value=["django.urls.resolve"],
):
p = subprocess.run(
[
sys.executable,
"-m",
"django",
"shell",
"-c",
"print(globals())",
"-v",
verbosity,
],
capture_output=True,
env=test_environ,
text=True,
umask=-1,
)
assertError(error, p.stdout)
self.assertNotIn("resolve", p.stdout)
@unittest.skipIf(
sys.platform == "win32", "Windows select() doesn't support file descriptors."
)
@ -50,7 +112,7 @@ class ShellCommandTestCase(SimpleTestCase):
with captured_stdin() as stdin, captured_stdout() as stdout:
stdin.write("print(100)\n")
stdin.seek(0)
call_command("shell")
call_command("shell", verbosity=0)
self.assertEqual(stdout.getvalue().strip(), "100")
@unittest.skipIf(
@ -62,7 +124,7 @@ class ShellCommandTestCase(SimpleTestCase):
with captured_stdin() as stdin, captured_stdout() as stdout:
stdin.write(self.script_globals)
stdin.seek(0)
call_command("shell")
call_command("shell", verbosity=0)
self.assertEqual(stdout.getvalue().strip(), "True")
@unittest.skipIf(
@ -74,7 +136,7 @@ class ShellCommandTestCase(SimpleTestCase):
with captured_stdin() as stdin, captured_stdout() as stdout:
stdin.write(self.script_with_inline_function)
stdin.seek(0)
call_command("shell")
call_command("shell", verbosity=0)
self.assertEqual(stdout.getvalue().strip(), __version__)
def test_ipython(self):