From 01013d51771aca3ba777b1c50f1686148b644390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Hovm=C3=B6ller?= Date: Sun, 13 Feb 2022 21:29:36 +0100 Subject: [PATCH 1/6] Nice 404 error message --- src/whitenoise/middleware.py | 13 +++++++++++++ tests/django_urls.py | 11 ++++++++++- tests/test_django_whitenoise.py | 19 +++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/whitenoise/middleware.py b/src/whitenoise/middleware.py index 248339dc..2deba90d 100644 --- a/src/whitenoise/middleware.py +++ b/src/whitenoise/middleware.py @@ -11,6 +11,7 @@ from django.urls import get_script_prefix from .base import WhiteNoise +from .responders import MissingFileError from .string_utils import decode_if_byte_string, ensure_leading_trailing_slash __all__ = ["WhiteNoiseMiddleware"] @@ -68,6 +69,18 @@ def process_request(self, request): if static_file is not None: return self.serve(static_file, request) + if settings.DEBUG and request.path.startswith(settings.STATIC_URL): + from django.contrib.staticfiles.finders import get_finders + finders = get_finders() + app_dirs = [] + for finder in finders: + for storage in finder.storages.values(): + app_dirs.append(storage.location) + app_dirs = "\n ".join(sorted(app_dirs)) + raise MissingFileError(f"""{request.path} not found. Searched these paths: + + {app_dirs}""") + @staticmethod def serve(static_file, request): response = static_file.get_response(request.method, request.META) diff --git a/tests/django_urls.py b/tests/django_urls.py index 562b8d6f..f4ce2222 100644 --- a/tests/django_urls.py +++ b/tests/django_urls.py @@ -1,3 +1,12 @@ from __future__ import annotations -urlpatterns = [] +from django.urls import path + + +def avoid_django_default_welcome_page(): + pass + + +urlpatterns = [ + path('', avoid_django_default_welcome_page) +] diff --git a/tests/test_django_whitenoise.py b/tests/test_django_whitenoise.py index c35e5413..995314a2 100644 --- a/tests/test_django_whitenoise.py +++ b/tests/test_django_whitenoise.py @@ -3,6 +3,7 @@ import shutil import tempfile from contextlib import closing +from pathlib import Path from urllib.parse import urljoin, urlparse import django @@ -210,3 +211,21 @@ def test_relative_static_url(server, static_files, _collect_static): url = storage.staticfiles_storage.url(static_files.js_path) response = server.get(url) assert response.content == static_files.js_content + + +def test_404_in_prod(server): + response = server.get(settings.STATIC_URL + 'garbage') + assert response.status_code == 404 + + +@override_settings(DEBUG=True) +def test_error_message(server): + response = server.get(settings.STATIC_URL + 'garbage') + print(response.content.decode()) + app_dirs = Path(__file__).parent / 'test_files' / 'static' + + expected = f"""{settings.STATIC_URL + 'garbage'} not found. Searched these paths: + + {app_dirs}""" + + assert expected in str(response.content.decode()) From a7c983710b783b54a801da92475c114ebc30099a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 19 Feb 2022 09:07:28 +0000 Subject: [PATCH 2/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/whitenoise/middleware.py | 7 +++++-- tests/django_urls.py | 4 +--- tests/test_django_whitenoise.py | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/whitenoise/middleware.py b/src/whitenoise/middleware.py index 2deba90d..9f8d8d6e 100644 --- a/src/whitenoise/middleware.py +++ b/src/whitenoise/middleware.py @@ -71,15 +71,18 @@ def process_request(self, request): if settings.DEBUG and request.path.startswith(settings.STATIC_URL): from django.contrib.staticfiles.finders import get_finders + finders = get_finders() app_dirs = [] for finder in finders: for storage in finder.storages.values(): app_dirs.append(storage.location) app_dirs = "\n ".join(sorted(app_dirs)) - raise MissingFileError(f"""{request.path} not found. Searched these paths: + raise MissingFileError( + f"""{request.path} not found. Searched these paths: - {app_dirs}""") + {app_dirs}""" + ) @staticmethod def serve(static_file, request): diff --git a/tests/django_urls.py b/tests/django_urls.py index f4ce2222..157b9b17 100644 --- a/tests/django_urls.py +++ b/tests/django_urls.py @@ -7,6 +7,4 @@ def avoid_django_default_welcome_page(): pass -urlpatterns = [ - path('', avoid_django_default_welcome_page) -] +urlpatterns = [path("", avoid_django_default_welcome_page)] diff --git a/tests/test_django_whitenoise.py b/tests/test_django_whitenoise.py index 995314a2..9f521fe1 100644 --- a/tests/test_django_whitenoise.py +++ b/tests/test_django_whitenoise.py @@ -214,15 +214,15 @@ def test_relative_static_url(server, static_files, _collect_static): def test_404_in_prod(server): - response = server.get(settings.STATIC_URL + 'garbage') + response = server.get(settings.STATIC_URL + "garbage") assert response.status_code == 404 @override_settings(DEBUG=True) def test_error_message(server): - response = server.get(settings.STATIC_URL + 'garbage') + response = server.get(settings.STATIC_URL + "garbage") print(response.content.decode()) - app_dirs = Path(__file__).parent / 'test_files' / 'static' + app_dirs = Path(__file__).parent / "test_files" / "static" expected = f"""{settings.STATIC_URL + 'garbage'} not found. Searched these paths: From 0f12aa38de1d99c8eff28cbdd1e06ee3e0ebb33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Hovm=C3=B6ller?= Date: Sat, 19 Feb 2022 10:11:36 +0100 Subject: [PATCH 3/6] Changelog --- docs/changelog.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 817108a2..a78b5f06 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,12 @@ Changelog ========= +Pending +------- + +* A nicer and more helpful error page in debug instead of just a 404 (Django only) + + 6.0.0 (2022-02-10) ------------------ From e778e1c11b2f830b5158d6483a4bb1179c7256bd Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Fri, 30 Aug 2024 00:36:55 -0700 Subject: [PATCH 4/6] Fix tests and better error message --- src/servestatic/middleware.py | 4 ++-- tests/test_django_whitenoise.py | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/servestatic/middleware.py b/src/servestatic/middleware.py index e45fa2bf..66eca968 100644 --- a/src/servestatic/middleware.py +++ b/src/servestatic/middleware.py @@ -184,9 +184,9 @@ async def __call__(self, request): for finder in current_finders for storage in finder.storages.values() ] - app_dirs = "\n ".join(sorted(app_dirs)) + app_dirs = "\n• ".join(sorted(app_dirs)) raise MissingFileError( - f"{request.path} not found. Searched these paths:{app_dirs}" + f"ServeStatic did not find the file '{request.path.lstrip(settings.STATIC_URL)}' within the following paths:\n• {app_dirs}" ) return await self.get_response(request) diff --git a/tests/test_django_whitenoise.py b/tests/test_django_whitenoise.py index 36c2ecd8..9cb5e219 100644 --- a/tests/test_django_whitenoise.py +++ b/tests/test_django_whitenoise.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +import html import shutil import tempfile from contextlib import closing @@ -248,12 +249,16 @@ def test_404_in_prod(server): @override_settings(DEBUG=True) def test_error_message(server): - response = server.get(settings.STATIC_URL + "garbage") - print(response.content.decode()) - app_dirs = Path(__file__).parent / "test_files" / "static" - - expected = f"""{settings.STATIC_URL + 'garbage'} not found. Searched these paths: + response = server.get(f"{settings.STATIC_URL}garbage") + response_content = str(response.content.decode()) + response_content = html.unescape(response_content) - {app_dirs}""" + # Beautify for easier debugging + response_content = response_content[response_content.index("ServeStatic") :] - assert expected in str(response.content.decode()) + assert ( + "ServeStatic did not find the file 'garbage' within the following paths:" + in response_content + ) + assert "•" in response_content + assert str(Path(__file__).parent / "test_files" / "static") in response_content From 1d2a9d17c84e4f83aae21ce331b29229ca98d130 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Fri, 30 Aug 2024 00:39:59 -0700 Subject: [PATCH 5/6] more robust tests --- tests/test_django_whitenoise.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_django_whitenoise.py b/tests/test_django_whitenoise.py index 9cb5e219..3dfa862f 100644 --- a/tests/test_django_whitenoise.py +++ b/tests/test_django_whitenoise.py @@ -244,7 +244,14 @@ def test_relative_static_url(server, static_files, _collect_static): def test_404_in_prod(server): response = server.get(settings.STATIC_URL + "garbage") + response_content = str(response.content.decode()) + response_content = html.unescape(response_content) + assert response.status_code == 404 + assert ( + "ServeStatic did not find the file 'garbage' within the following paths:" + not in response_content + ) @override_settings(DEBUG=True) From 25e8b6ba27cd5ae756ecfc5a8b3b2fc002b2e8f1 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Fri, 30 Aug 2024 00:43:49 -0700 Subject: [PATCH 6/6] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3f4031e..fd9f9ff0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,10 @@ Using the following categories, list your changes in this order: ## [Unreleased](https://github.com/Archmonger/ServeStatic/compare/1.1.0...HEAD) +### Added + +- Verbose Django `404` error page when `settings.py:DEBUG` is `True` ([Upstream PR](https://github.com/evansd/whitenoise/pull/366)) + ### Fixed - Fix compatibility with third-party sync only middleware