diff options
| -rw-r--r-- | AUTHORS | 1 | ||||
| -rw-r--r-- | django/urls/resolvers.py | 33 | ||||
| -rw-r--r-- | docs/ref/checks.txt | 2 | ||||
| -rw-r--r-- | tests/check_framework/test_urls.py | 41 | ||||
| -rw-r--r-- | tests/check_framework/urls/path_compatibility/matched_angle_brackets.py | 5 | ||||
| -rw-r--r-- | tests/check_framework/urls/path_compatibility/unmatched_angle_brackets.py | 8 |
6 files changed, 89 insertions, 1 deletions
@@ -62,6 +62,7 @@ answer newbie questions, and generally made Django that much better: Aljaž Košir <aljazkosir5@gmail.com> Aljosa Mohorovic <aljosa.mohorovic@gmail.com> Alokik Vijay <alokik.roe@gmail.com> + Amir Karimi <amk9978@gmail.com> Amit Chakradeo <https://amit.chakradeo.net/> Amit Ramon <amit.ramon@gmail.com> Amit Upadhyay <http://www.amitu.com/blog/> diff --git a/django/urls/resolvers.py b/django/urls/resolvers.py index b021673772..432bf50076 100644 --- a/django/urls/resolvers.py +++ b/django/urls/resolvers.py @@ -318,7 +318,10 @@ class RoutePattern(CheckURLMixin): return None def check(self): - warnings = self._check_pattern_startswith_slash() + warnings = [ + *self._check_pattern_startswith_slash(), + *self._check_pattern_unmatched_angle_brackets(), + ] route = self._route if "(?P<" in route or route.startswith("^") or route.endswith("$"): warnings.append( @@ -331,6 +334,34 @@ class RoutePattern(CheckURLMixin): ) return warnings + def _check_pattern_unmatched_angle_brackets(self): + warnings = [] + segments = self._route.split("/") + for segment in segments: + open_bracket_counter = 0 + unmatched_angle_bracket = None + for char in segment: + if char == "<": + open_bracket_counter += 1 + elif char == ">": + open_bracket_counter -= 1 + if open_bracket_counter < 0: + unmatched_angle_bracket = ">" + break + else: + if open_bracket_counter > 0: + unmatched_angle_bracket = "<" + + if unmatched_angle_bracket is not None: + warnings.append( + Warning( + "Your URL pattern %s has an unmatched '%s' bracket." + % (self.describe(), unmatched_angle_bracket), + id="urls.W010", + ) + ) + return warnings + def _compile(self, route): return re.compile(_route_to_regex(route, self._is_endpoint)[0]) diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index de7b0dc382..49bc9cb0a2 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -609,6 +609,8 @@ The following checks are performed on your URL configuration: imported. * **urls.E009**: Your URL pattern ``<pattern>`` has an invalid view, pass ``<view>.as_view()`` instead of ``<view>``. +* **urls.W010**: Your URL pattern ``<pattern>` has an unmatched + ``<angle bracket>``. ``contrib`` app checks ====================== diff --git a/tests/check_framework/test_urls.py b/tests/check_framework/test_urls.py index a9038c6f13..4b6a4a6f3e 100644 --- a/tests/check_framework/test_urls.py +++ b/tests/check_framework/test_urls.py @@ -161,6 +161,47 @@ class CheckUrlConfigTests(SimpleTestCase): ], ) + @override_settings( + ROOT_URLCONF="check_framework.urls.path_compatibility.matched_angle_brackets" + ) + def test_no_warnings_matched_angle_brackets(self): + self.assertEqual(check_url_config(None), []) + + @override_settings( + ROOT_URLCONF="check_framework.urls.path_compatibility.unmatched_angle_brackets" + ) + def test_warning_unmatched_angle_brackets(self): + self.assertEqual( + check_url_config(None), + [ + Warning( + "Your URL pattern 'beginning-with/<angle_bracket' has an unmatched " + "'<' bracket.", + id="urls.W010", + ), + Warning( + "Your URL pattern 'ending-with/angle_bracket>' has an unmatched " + "'>' bracket.", + id="urls.W010", + ), + Warning( + "Your URL pattern 'closed_angle>/x/<opened_angle' has an unmatched " + "'>' bracket.", + id="urls.W010", + ), + Warning( + "Your URL pattern 'closed_angle>/x/<opened_angle' has an unmatched " + "'<' bracket.", + id="urls.W010", + ), + Warning( + "Your URL pattern '<mixed>angle_bracket>' has an unmatched '>' " + "bracket.", + id="urls.W010", + ), + ], + ) + class UpdatedToPathTests(SimpleTestCase): @override_settings( diff --git a/tests/check_framework/urls/path_compatibility/matched_angle_brackets.py b/tests/check_framework/urls/path_compatibility/matched_angle_brackets.py new file mode 100644 index 0000000000..de3076942d --- /dev/null +++ b/tests/check_framework/urls/path_compatibility/matched_angle_brackets.py @@ -0,0 +1,5 @@ +from django.urls import path + +urlpatterns = [ + path("<int:angle_bracket>", lambda x: x), +] diff --git a/tests/check_framework/urls/path_compatibility/unmatched_angle_brackets.py b/tests/check_framework/urls/path_compatibility/unmatched_angle_brackets.py new file mode 100644 index 0000000000..e53685ec7a --- /dev/null +++ b/tests/check_framework/urls/path_compatibility/unmatched_angle_brackets.py @@ -0,0 +1,8 @@ +from django.urls import path + +urlpatterns = [ + path("beginning-with/<angle_bracket", lambda x: x), + path("ending-with/angle_bracket>", lambda x: x), + path("closed_angle>/x/<opened_angle", lambda x: x), + path("<mixed>angle_bracket>", lambda x: x), +] |
