diff options
| author | Alasdair Nicol <alasdair@thenicols.net> | 2015-09-16 23:07:39 +0100 |
|---|---|---|
| committer | Josh Smeaton <josh.smeaton@gmail.com> | 2015-09-21 23:46:21 +1000 |
| commit | fe3fc5210f0bb334a679ed420152af1c862c0239 (patch) | |
| tree | dc41897b19aaac166e62dd455334faa28fad0bab /django | |
| parent | 2f53d342f1ce2fb36cddbd90e24c4fda86389c27 (diff) | |
Fixed #23813 -- Added checks for common URL pattern errors
Thanks jwa and lamby for the suggestions, and timgraham and jarshwah
for their reviews.
Diffstat (limited to 'django')
| -rw-r--r-- | django/core/checks/__init__.py | 2 | ||||
| -rw-r--r-- | django/core/checks/registry.py | 1 | ||||
| -rw-r--r-- | django/core/checks/urls.py | 87 | ||||
| -rw-r--r-- | django/core/urlresolvers.py | 2 |
4 files changed, 91 insertions, 1 deletions
diff --git a/django/core/checks/__init__.py b/django/core/checks/__init__.py index dec2446ae2..cd21744327 100644 --- a/django/core/checks/__init__.py +++ b/django/core/checks/__init__.py @@ -15,6 +15,8 @@ import django.core.checks.security.base # NOQA isort:skip import django.core.checks.security.csrf # NOQA isort:skip import django.core.checks.security.sessions # NOQA isort:skip import django.core.checks.templates # NOQA isort:skip +import django.core.checks.urls # NOQA isort:skip + __all__ = [ 'CheckMessage', diff --git a/django/core/checks/registry.py b/django/core/checks/registry.py index 96df429dc9..3e3d45fff8 100644 --- a/django/core/checks/registry.py +++ b/django/core/checks/registry.py @@ -17,6 +17,7 @@ class Tags(object): security = 'security' signals = 'signals' templates = 'templates' + urls = 'urls' class CheckRegistry(object): diff --git a/django/core/checks/urls.py b/django/core/checks/urls.py new file mode 100644 index 0000000000..a0d5b4a825 --- /dev/null +++ b/django/core/checks/urls.py @@ -0,0 +1,87 @@ +from __future__ import unicode_literals + +from . import Tags, Warning, register + + +@register(Tags.urls) +def check_url_config(app_configs, **kwargs): + from django.core.urlresolvers import get_resolver + resolver = get_resolver() + return check_resolver(resolver) + + +def check_resolver(resolver): + """ + Recursively check the resolver + """ + from django.core.urlresolvers import RegexURLPattern, RegexURLResolver + warnings = [] + for pattern in resolver.url_patterns: + if isinstance(pattern, RegexURLResolver): + warnings.extend(check_include_trailing_dollar(pattern)) + # Check resolver recursively + warnings.extend(check_resolver(pattern)) + elif isinstance(pattern, RegexURLPattern): + warnings.extend(check_pattern_name(pattern)) + + warnings.extend(check_pattern_startswith_slash(pattern)) + + return warnings + + +def describe_pattern(pattern): + """ + Formats the URL pattern for display in warning messages + """ + description = "'{}'".format(pattern.regex.pattern) + if getattr(pattern, 'name', False): + description += " [name='{}']".format(pattern.name) + return description + + +def check_include_trailing_dollar(pattern): + """ + Checks that include is not used with a regex ending with a dollar. + """ + regex_pattern = pattern.regex.pattern + if regex_pattern.endswith('$') and not regex_pattern.endswith('\$'): + warning = Warning( + "Your URL pattern {} uses include with a regex ending with a '$'. " + "Remove the dollar from the regex to avoid problems including " + "URLs.".format(describe_pattern(pattern)), + id="urls.W001", + ) + return [warning] + else: + return [] + + +def check_pattern_startswith_slash(pattern): + """ + Checks that the pattern does not begin with a forward slash + """ + regex_pattern = pattern.regex.pattern + if regex_pattern.startswith('/') or regex_pattern.startswith('^/'): + warning = Warning( + "Your URL pattern {} has a regex beginning with a '/'. " + "Remove this slash as it is unnecessary.".format(describe_pattern(pattern)), + id="urls.W002", + ) + return [warning] + else: + return [] + + +def check_pattern_name(pattern): + """ + Checks that the pattern name does not contain a colon + """ + if pattern.name is not None and ":" in pattern.name: + warning = Warning( + "Your URL pattern {} has a name including a ':'. Remove the colon, to " + "avoid ambiguous namespace references.".format(describe_pattern(pattern)), + id="urls.W003", + ) + return [warning] + else: + return [] diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py index e462c343dc..3554878865 100644 --- a/django/core/urlresolvers.py +++ b/django/core/urlresolvers.py @@ -146,7 +146,7 @@ def get_callable(lookup_view, can_fail=False): @lru_cache.lru_cache(maxsize=None) -def get_resolver(urlconf): +def get_resolver(urlconf=None): if urlconf is None: from django.conf import settings urlconf = settings.ROOT_URLCONF |
