summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Johnson <me@adamj.eu>2024-02-19 04:58:37 +0000
committerGitHub <noreply@github.com>2024-02-19 05:58:37 +0100
commit28a3fbe0048883fdd5cefd6ffecb88e351121891 (patch)
treeb4d3add3928185f09c030768d85a17b6524dfafb
parent5e80390add100e0c7a1ac8e51739f94c5d706ea3 (diff)
Fixed #35229 -- Made URL custom error handler check run once.
-rw-r--r--django/core/checks/urls.py42
-rw-r--r--django/urls/resolvers.py34
-rw-r--r--tests/check_framework/test_urls.py11
3 files changed, 49 insertions, 38 deletions
diff --git a/django/core/checks/urls.py b/django/core/checks/urls.py
index 34eff9671d..aef2bfebb0 100644
--- a/django/core/checks/urls.py
+++ b/django/core/checks/urls.py
@@ -1,6 +1,8 @@
+import inspect
from collections import Counter
from django.conf import settings
+from django.core.exceptions import ViewDoesNotExist
from . import Error, Tags, Warning, register
@@ -115,3 +117,43 @@ def E006(name):
"The {} setting must end with a slash.".format(name),
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
diff --git a/django/urls/resolvers.py b/django/urls/resolvers.py
index 3607c84228..5f9941dd65 100644
--- a/django/urls/resolvers.py
+++ b/django/urls/resolvers.py
@@ -19,7 +19,7 @@ from asgiref.local import Local
from django.conf import settings
from django.core.checks import Error, Warning
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.functional import cached_property
from django.utils.http import RFC3986_SUBDELIMS, escape_leading_slashes
@@ -518,40 +518,8 @@ class URLResolver:
messages = []
for pattern in self.url_patterns:
messages.extend(check_resolver(pattern))
- messages.extend(self._check_custom_error_handlers())
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):
# Short-circuit if called recursively in this thread to prevent
# infinite recursion. Concurrent threads may call this at the same
diff --git a/tests/check_framework/test_urls.py b/tests/check_framework/test_urls.py
index 4b6a4a6f3e..a31c5fd856 100644
--- a/tests/check_framework/test_urls.py
+++ b/tests/check_framework/test_urls.py
@@ -2,6 +2,7 @@ from django.conf import settings
from django.core.checks.messages import Error, Warning
from django.core.checks.urls import (
E006,
+ check_custom_error_handlers,
check_url_config,
check_url_namespaces_unique,
check_url_settings,
@@ -243,7 +244,7 @@ class CheckCustomErrorHandlersTests(SimpleTestCase):
ROOT_URLCONF="check_framework.urls.bad_function_based_error_handlers",
)
def test_bad_function_based_handlers(self):
- result = check_url_config(None)
+ result = check_custom_error_handlers(None)
self.assertEqual(len(result), 4)
for code, num_params, error in zip([400, 403, 404, 500], [2, 2, 2, 1], result):
with self.subTest("handler{}".format(code)):
@@ -264,7 +265,7 @@ class CheckCustomErrorHandlersTests(SimpleTestCase):
ROOT_URLCONF="check_framework.urls.bad_class_based_error_handlers",
)
def test_bad_class_based_handlers(self):
- result = check_url_config(None)
+ result = check_custom_error_handlers(None)
self.assertEqual(len(result), 4)
for code, num_params, error in zip([400, 403, 404, 500], [2, 2, 2, 1], result):
with self.subTest("handler%s" % code):
@@ -287,7 +288,7 @@ class CheckCustomErrorHandlersTests(SimpleTestCase):
ROOT_URLCONF="check_framework.urls.bad_error_handlers_invalid_path"
)
def test_bad_handlers_invalid_path(self):
- result = check_url_config(None)
+ result = check_custom_error_handlers(None)
paths = [
"django.views.bad_handler",
"django.invalid_module.bad_handler",
@@ -318,14 +319,14 @@ class CheckCustomErrorHandlersTests(SimpleTestCase):
ROOT_URLCONF="check_framework.urls.good_function_based_error_handlers",
)
def test_good_function_based_handlers(self):
- result = check_url_config(None)
+ result = check_custom_error_handlers(None)
self.assertEqual(result, [])
@override_settings(
ROOT_URLCONF="check_framework.urls.good_class_based_error_handlers",
)
def test_good_class_based_handlers(self):
- result = check_url_config(None)
+ result = check_custom_error_handlers(None)
self.assertEqual(result, [])