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

Fixed #35229 -- Made URL custom error handler check run once.

This commit is contained in:
Adam Johnson 2024-02-19 04:58:37 +00:00 committed by GitHub
parent 5e80390add
commit 28a3fbe004
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 49 additions and 38 deletions

View File

@ -1,6 +1,8 @@
import inspect
from collections import Counter from collections import Counter
from django.conf import settings from django.conf import settings
from django.core.exceptions import ViewDoesNotExist
from . import Error, Tags, Warning, register from . import Error, Tags, Warning, register
@ -115,3 +117,43 @@ def E006(name):
"The {} setting must end with a slash.".format(name), "The {} setting must end with a slash.".format(name),
id="urls.E006", id="urls.E006",
) )
@register(Tags.urls)
def check_custom_error_handlers(app_configs, **kwargs):
if not getattr(settings, "ROOT_URLCONF", None):
return []
from django.urls import get_resolver
resolver = get_resolver()
errors = []
# All handlers take (request, exception) arguments except handler500
# which takes (request).
for status_code, num_parameters in [(400, 2), (403, 2), (404, 2), (500, 1)]:
try:
handler = resolver.resolve_error_handler(status_code)
except (ImportError, ViewDoesNotExist) as e:
path = getattr(resolver.urlconf_module, "handler%s" % status_code)
msg = (
"The custom handler{status_code} view '{path}' could not be "
"imported."
).format(status_code=status_code, path=path)
errors.append(Error(msg, hint=str(e), id="urls.E008"))
continue
signature = inspect.signature(handler)
args = [None] * num_parameters
try:
signature.bind(*args)
except TypeError:
msg = (
"The custom handler{status_code} view '{path}' does not "
"take the correct number of arguments ({args})."
).format(
status_code=status_code,
path=handler.__module__ + "." + handler.__qualname__,
args="request, exception" if num_parameters == 2 else "request",
)
errors.append(Error(msg, id="urls.E007"))
return errors

View File

@ -19,7 +19,7 @@ from asgiref.local import Local
from django.conf import settings from django.conf import settings
from django.core.checks import Error, Warning from django.core.checks import Error, Warning
from django.core.checks.urls import check_resolver from django.core.checks.urls import check_resolver
from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist from django.core.exceptions import ImproperlyConfigured
from django.utils.datastructures import MultiValueDict from django.utils.datastructures import MultiValueDict
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.http import RFC3986_SUBDELIMS, escape_leading_slashes from django.utils.http import RFC3986_SUBDELIMS, escape_leading_slashes
@ -518,40 +518,8 @@ class URLResolver:
messages = [] messages = []
for pattern in self.url_patterns: for pattern in self.url_patterns:
messages.extend(check_resolver(pattern)) messages.extend(check_resolver(pattern))
messages.extend(self._check_custom_error_handlers())
return messages or self.pattern.check() return messages or self.pattern.check()
def _check_custom_error_handlers(self):
messages = []
# All handlers take (request, exception) arguments except handler500
# which takes (request).
for status_code, num_parameters in [(400, 2), (403, 2), (404, 2), (500, 1)]:
try:
handler = self.resolve_error_handler(status_code)
except (ImportError, ViewDoesNotExist) as e:
path = getattr(self.urlconf_module, "handler%s" % status_code)
msg = (
"The custom handler{status_code} view '{path}' could not be "
"imported."
).format(status_code=status_code, path=path)
messages.append(Error(msg, hint=str(e), id="urls.E008"))
continue
signature = inspect.signature(handler)
args = [None] * num_parameters
try:
signature.bind(*args)
except TypeError:
msg = (
"The custom handler{status_code} view '{path}' does not "
"take the correct number of arguments ({args})."
).format(
status_code=status_code,
path=handler.__module__ + "." + handler.__qualname__,
args="request, exception" if num_parameters == 2 else "request",
)
messages.append(Error(msg, id="urls.E007"))
return messages
def _populate(self): def _populate(self):
# Short-circuit if called recursively in this thread to prevent # Short-circuit if called recursively in this thread to prevent
# infinite recursion. Concurrent threads may call this at the same # infinite recursion. Concurrent threads may call this at the same

View File

@ -2,6 +2,7 @@ from django.conf import settings
from django.core.checks.messages import Error, Warning from django.core.checks.messages import Error, Warning
from django.core.checks.urls import ( from django.core.checks.urls import (
E006, E006,
check_custom_error_handlers,
check_url_config, check_url_config,
check_url_namespaces_unique, check_url_namespaces_unique,
check_url_settings, check_url_settings,
@ -243,7 +244,7 @@ class CheckCustomErrorHandlersTests(SimpleTestCase):
ROOT_URLCONF="check_framework.urls.bad_function_based_error_handlers", ROOT_URLCONF="check_framework.urls.bad_function_based_error_handlers",
) )
def test_bad_function_based_handlers(self): def test_bad_function_based_handlers(self):
result = check_url_config(None) result = check_custom_error_handlers(None)
self.assertEqual(len(result), 4) self.assertEqual(len(result), 4)
for code, num_params, error in zip([400, 403, 404, 500], [2, 2, 2, 1], result): for code, num_params, error in zip([400, 403, 404, 500], [2, 2, 2, 1], result):
with self.subTest("handler{}".format(code)): with self.subTest("handler{}".format(code)):
@ -264,7 +265,7 @@ class CheckCustomErrorHandlersTests(SimpleTestCase):
ROOT_URLCONF="check_framework.urls.bad_class_based_error_handlers", ROOT_URLCONF="check_framework.urls.bad_class_based_error_handlers",
) )
def test_bad_class_based_handlers(self): def test_bad_class_based_handlers(self):
result = check_url_config(None) result = check_custom_error_handlers(None)
self.assertEqual(len(result), 4) self.assertEqual(len(result), 4)
for code, num_params, error in zip([400, 403, 404, 500], [2, 2, 2, 1], result): for code, num_params, error in zip([400, 403, 404, 500], [2, 2, 2, 1], result):
with self.subTest("handler%s" % code): with self.subTest("handler%s" % code):
@ -287,7 +288,7 @@ class CheckCustomErrorHandlersTests(SimpleTestCase):
ROOT_URLCONF="check_framework.urls.bad_error_handlers_invalid_path" ROOT_URLCONF="check_framework.urls.bad_error_handlers_invalid_path"
) )
def test_bad_handlers_invalid_path(self): def test_bad_handlers_invalid_path(self):
result = check_url_config(None) result = check_custom_error_handlers(None)
paths = [ paths = [
"django.views.bad_handler", "django.views.bad_handler",
"django.invalid_module.bad_handler", "django.invalid_module.bad_handler",
@ -318,14 +319,14 @@ class CheckCustomErrorHandlersTests(SimpleTestCase):
ROOT_URLCONF="check_framework.urls.good_function_based_error_handlers", ROOT_URLCONF="check_framework.urls.good_function_based_error_handlers",
) )
def test_good_function_based_handlers(self): def test_good_function_based_handlers(self):
result = check_url_config(None) result = check_custom_error_handlers(None)
self.assertEqual(result, []) self.assertEqual(result, [])
@override_settings( @override_settings(
ROOT_URLCONF="check_framework.urls.good_class_based_error_handlers", ROOT_URLCONF="check_framework.urls.good_class_based_error_handlers",
) )
def test_good_class_based_handlers(self): def test_good_class_based_handlers(self):
result = check_url_config(None) result = check_custom_error_handlers(None)
self.assertEqual(result, []) self.assertEqual(result, [])