1
0
mirror of https://github.com/django/django.git synced 2024-12-22 17:16:24 +00:00

Fixed #32915 -- Refactored runserver command to restore traceback

The traceback for `runserver --nostatic` with broken/missing
    settings was hidden because the custom runserver command which
    added `--nostatic` to the command args was only instantiated if
    the settings had no errors.
This commit is contained in:
Paul Schilling 2024-06-08 18:40:25 +02:00
parent 3556f63c4c
commit fab52fc571
5 changed files with 57 additions and 62 deletions

View File

@ -1,36 +0,0 @@
from django.conf import settings
from django.contrib.staticfiles.handlers import StaticFilesHandler
from django.core.management.commands.runserver import Command as RunserverCommand
class Command(RunserverCommand):
help = (
"Starts a lightweight web server for development and also serves static files."
)
def add_arguments(self, parser):
super().add_arguments(parser)
parser.add_argument(
"--nostatic",
action="store_false",
dest="use_static_handler",
help="Tells Django to NOT automatically serve static files at STATIC_URL.",
)
parser.add_argument(
"--insecure",
action="store_true",
dest="insecure_serving",
help="Allows serving static files even if DEBUG is False.",
)
def get_handler(self, *args, **options):
"""
Return the static files serving handler wrapping the default handler,
if static files should be served. Otherwise return the default handler.
"""
handler = super().get_handler(*args, **options)
use_static_handler = options["use_static_handler"]
insecure_serving = options["insecure_serving"]
if use_static_handler and (settings.DEBUG or insecure_serving):
return StaticFilesHandler(handler)
return handler

View File

@ -6,6 +6,7 @@ import sys
from datetime import datetime
from django.conf import settings
from django.contrib.staticfiles.handlers import StaticFilesHandler
from django.core.management.base import BaseCommand, CommandError
from django.core.servers.basehttp import WSGIServer, get_internal_wsgi_application, run
from django.db import connections
@ -65,6 +66,18 @@ class Command(BaseCommand):
action="store_true",
help="Skip system checks.",
)
parser.add_argument(
"--nostatic",
action="store_false",
dest="use_static_handler",
help="Tells Django to NOT automatically serve static files at STATIC_URL.",
)
parser.add_argument(
"--insecure",
action="store_true",
dest="insecure_serving",
help="Allows serving static files even if DEBUG is False.",
)
def execute(self, *args, **options):
if options["no_color"]:
@ -75,8 +88,18 @@ class Command(BaseCommand):
super().execute(*args, **options)
def get_handler(self, *args, **options):
"""Return the default WSGI handler for the runner."""
return get_internal_wsgi_application()
"""
Return the default WSGI handler for the runner, or `StaticFilesHandler` if
either of --nostatic/--insecure is passed
"""
handler = get_internal_wsgi_application()
use_static_handler = options["use_static_handler"]
insecure_serving = options["insecure_serving"]
if use_static_handler and (settings.DEBUG or insecure_serving):
handler = StaticFilesHandler(handler)
return handler
def handle(self, *args, **options):
if not settings.DEBUG and not settings.ALLOWED_HOSTS:

View File

@ -30,7 +30,13 @@ from django.core.management.commands.runserver import Command as RunserverComman
from django.core.management.commands.testserver import Command as TestserverCommand
from django.db import ConnectionHandler, connection
from django.db.migrations.recorder import MigrationRecorder
from django.test import LiveServerTestCase, SimpleTestCase, TestCase, override_settings
from django.test import (
LiveServerTestCase,
RequestFactory,
SimpleTestCase,
TestCase,
override_settings,
)
from django.test.utils import captured_stderr, captured_stdout
from django.urls import path
from django.utils.version import PY313
@ -1695,6 +1701,23 @@ class ManageRunserver(SimpleTestCase):
self.assertIn("Performing system checks...", self.output.getvalue())
mocked_check.assert_called()
@override_settings(MIDDLEWARE=["django.middleware.common.CommonMiddleware"])
def test_middleware_loaded_only_once(self):
with mock.patch("django.middleware.common.CommonMiddleware") as mocked:
self.cmd.get_handler(use_static_handler=True, insecure_serving=True)
self.assertEqual(mocked.call_count, 1)
def test_404_response(self):
handler = self.cmd.get_handler(use_static_handler=True, insecure_serving=True)
missing_static_file = os.path.join(settings.STATIC_URL, "unknown.css")
req = RequestFactory().get(missing_static_file)
with override_settings(DEBUG=False):
response = handler.get_response(req)
self.assertEqual(response.status_code, 404)
with override_settings(DEBUG=True):
response = handler.get_response(req)
self.assertEqual(response.status_code, 404)
class ManageRunserverMigrationWarning(TestCase):
def setUp(self):

View File

@ -338,6 +338,12 @@ class SettingsTests(SimpleTestCase):
with self.assertRaisesMessage(ValueError, "Incorrect timezone setting: test"):
settings._setup()
def test_import_non_existing_module(self):
exc = ModuleNotFoundError("No module named 'fake_module'", name="fake_module")
with mock.patch("importlib.import_module", side_effect=exc):
with self.assertRaisesMessage(ImportError, "No module named 'fake_module'"):
Settings("fake_settings_module")
class TestComplexSettingOverride(SimpleTestCase):
def setUp(self):

View File

@ -11,11 +11,11 @@ from admin_scripts.tests import AdminScriptTestCase
from django.conf import STATICFILES_STORAGE_ALIAS, settings
from django.contrib.staticfiles import storage
from django.contrib.staticfiles.management.commands import collectstatic, runserver
from django.contrib.staticfiles.management.commands import collectstatic
from django.core.exceptions import ImproperlyConfigured
from django.core.management import CommandError, call_command
from django.core.management.base import SystemCheckError
from django.test import RequestFactory, override_settings
from django.test import override_settings
from django.test.utils import extend_sys_path
from django.utils._os import symlinks_supported
from django.utils.functional import empty
@ -33,27 +33,6 @@ class TestNoFilesCreated:
self.assertEqual(os.listdir(settings.STATIC_ROOT), [])
class TestRunserver(StaticFilesTestCase):
@override_settings(MIDDLEWARE=["django.middleware.common.CommonMiddleware"])
def test_middleware_loaded_only_once(self):
command = runserver.Command()
with mock.patch("django.middleware.common.CommonMiddleware") as mocked:
command.get_handler(use_static_handler=True, insecure_serving=True)
self.assertEqual(mocked.call_count, 1)
def test_404_response(self):
command = runserver.Command()
handler = command.get_handler(use_static_handler=True, insecure_serving=True)
missing_static_file = os.path.join(settings.STATIC_URL, "unknown.css")
req = RequestFactory().get(missing_static_file)
with override_settings(DEBUG=False):
response = handler.get_response(req)
self.assertEqual(response.status_code, 404)
with override_settings(DEBUG=True):
response = handler.get_response(req)
self.assertEqual(response.status_code, 404)
class TestFindStatic(TestDefaults, CollectionTestCase):
"""
Test ``findstatic`` management command.