Fixed #29642 -- Added check for arguments of custom error handler views.
This commit is contained in:
parent
d483a5f0dc
commit
245c36d7b6
|
@ -6,13 +6,14 @@ a string) and returns a ResolverMatch object which provides access to all
|
||||||
attributes of the resolved URL match.
|
attributes of the resolved URL match.
|
||||||
"""
|
"""
|
||||||
import functools
|
import functools
|
||||||
|
import inspect
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.checks import 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
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.utils.datastructures import MultiValueDict
|
from django.utils.datastructures import MultiValueDict
|
||||||
|
@ -392,10 +393,33 @@ class URLResolver:
|
||||||
)
|
)
|
||||||
|
|
||||||
def check(self):
|
def check(self):
|
||||||
warnings = []
|
messages = []
|
||||||
for pattern in self.url_patterns:
|
for pattern in self.url_patterns:
|
||||||
warnings.extend(check_resolver(pattern))
|
messages.extend(check_resolver(pattern))
|
||||||
return warnings or self.pattern.check()
|
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)]:
|
||||||
|
handler, param_dict = self.resolve_error_handler(status_code)
|
||||||
|
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
|
||||||
|
|
|
@ -457,6 +457,8 @@ The following checks are performed on your URL configuration:
|
||||||
able to reverse all URLs in this namespace.
|
able to reverse all URLs in this namespace.
|
||||||
* **urls.E006**: The :setting:`MEDIA_URL`/ :setting:`STATIC_URL` setting must
|
* **urls.E006**: The :setting:`MEDIA_URL`/ :setting:`STATIC_URL` setting must
|
||||||
end with a slash.
|
end with a slash.
|
||||||
|
* **urls.E007**: The custom ``handlerXXX`` view ``'path.to.view'`` does not
|
||||||
|
take the correct number of arguments (…).
|
||||||
|
|
||||||
``contrib`` app checks
|
``contrib`` app checks
|
||||||
======================
|
======================
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.checks.messages import Warning
|
from django.core.checks.messages import Error, Warning
|
||||||
from django.core.checks.urls import (
|
from django.core.checks.urls import (
|
||||||
E006, check_url_config, check_url_namespaces_unique, check_url_settings,
|
E006, check_url_config, check_url_namespaces_unique, check_url_settings,
|
||||||
get_warning_for_invalid_pattern,
|
get_warning_for_invalid_pattern,
|
||||||
|
@ -165,6 +165,28 @@ class UpdatedToPathTests(SimpleTestCase):
|
||||||
self.assertIn(expected_msg, warning.msg)
|
self.assertIn(expected_msg, warning.msg)
|
||||||
|
|
||||||
|
|
||||||
|
class CheckCustomErrorHandlersTests(SimpleTestCase):
|
||||||
|
|
||||||
|
@override_settings(ROOT_URLCONF='check_framework.urls.bad_error_handlers')
|
||||||
|
def test_bad_handlers(self):
|
||||||
|
result = check_url_config(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)):
|
||||||
|
self.assertEqual(error, Error(
|
||||||
|
"The custom handler{} view "
|
||||||
|
"'check_framework.urls.bad_error_handlers.bad_handler' "
|
||||||
|
"does not take the correct number of arguments (request{})."
|
||||||
|
.format(code, ', exception' if num_params == 2 else ''),
|
||||||
|
id='urls.E007',
|
||||||
|
))
|
||||||
|
|
||||||
|
@override_settings(ROOT_URLCONF='check_framework.urls.good_error_handlers')
|
||||||
|
def test_good_handlers(self):
|
||||||
|
result = check_url_config(None)
|
||||||
|
self.assertEqual(result, [])
|
||||||
|
|
||||||
|
|
||||||
class CheckURLSettingsTests(SimpleTestCase):
|
class CheckURLSettingsTests(SimpleTestCase):
|
||||||
|
|
||||||
@override_settings(STATIC_URL='a/', MEDIA_URL='b/')
|
@override_settings(STATIC_URL='a/', MEDIA_URL='b/')
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
urlpatterns = []
|
||||||
|
|
||||||
|
handler400 = __name__ + '.bad_handler'
|
||||||
|
handler403 = __name__ + '.bad_handler'
|
||||||
|
handler404 = __name__ + '.bad_handler'
|
||||||
|
handler500 = __name__ + '.bad_handler'
|
||||||
|
|
||||||
|
|
||||||
|
def bad_handler():
|
||||||
|
pass
|
|
@ -0,0 +1,10 @@
|
||||||
|
urlpatterns = []
|
||||||
|
|
||||||
|
handler400 = __name__ + '.good_handler'
|
||||||
|
handler403 = __name__ + '.good_handler'
|
||||||
|
handler404 = __name__ + '.good_handler'
|
||||||
|
handler500 = __name__ + '.good_handler'
|
||||||
|
|
||||||
|
|
||||||
|
def good_handler(request, exception=None, foo='bar'):
|
||||||
|
pass
|
Loading…
Reference in New Issue