Fixed #27648 -- Deprecated (iLmsu) regex groups in url() patterns.

This commit is contained in:
Tim Graham 2016-12-27 15:59:13 -05:00 committed by GitHub
parent 544b2ef29f
commit 51cde873d9
7 changed files with 60 additions and 9 deletions

View File

@ -7,7 +7,10 @@ should be good enough for a large class of URLS, however.
"""
from __future__ import unicode_literals
import warnings
from django.utils import six
from django.utils.deprecation import RemovedInDjango21Warning
from django.utils.six.moves import zip
# Mapping of an escape character to a representative of that class. So, e.g.,
@ -59,9 +62,7 @@ def normalize(pattern):
(3) Select the first (essentially an arbitrary) element from any character
class. Select an arbitrary character for any unordered class (e.g. '.'
or '\w') in the pattern.
(4) Ignore comments, look-ahead and look-behind assertions, and any of the
reg-exp flags that won't change what we construct ("iLmsu"). "(?x)" is
an error, however.
(4) Ignore look-ahead and look-behind assertions.
(5) Raise an error on any disjunctive ('|') constructs.
Django's URLs for forward resolving are either all positional arguments or
@ -128,10 +129,16 @@ def normalize(pattern):
walk_to_end(ch, pattern_iter)
else:
ch, escaped = next(pattern_iter)
if ch in "iLmsu#!=<":
if ch in '!=<':
# All of these are ignorable. Walk to the end of the
# group.
walk_to_end(ch, pattern_iter)
elif ch in 'iLmsu#':
warnings.warn(
'Using (?%s) in url() patterns is deprecated.' % ch,
RemovedInDjango21Warning
)
walk_to_end(ch, pattern_iter)
elif ch == ':':
# Non-capturing group
non_capturing_groups.append(len(result))

View File

@ -48,6 +48,9 @@ details on these changes.
* The ``Model._meta.has_auto_field`` attribute will be removed.
* Support for regular expression groups with ``iLmsu#`` in ``url()`` will be
removed.
.. _deprecation-removed-in-2.0:
2.0

View File

@ -747,3 +747,10 @@ Miscellaneous
* ``Model._meta.has_auto_field`` is deprecated in favor of checking if
``Model._meta.auto_field is not None``.
* Using regular expression groups with ``iLmsu#`` in ``url()`` is deprecated.
The only group that's useful is ``(?i)`` for case-insensitive URLs, however,
case-insensitive URLs aren't a good practice because they create multiple
entries for search engines, for example. An alternative solution could be to
create a :data:`~django.conf.urls.handler404` that looks for uppercase
characters in the URL and redirects to a lowercase equivalent.

View File

@ -0,0 +1,33 @@
import warnings
from django.conf.urls import url
from django.test import SimpleTestCase, override_settings
from django.urls import reverse
from .views import empty_view
urlpatterns = [
url(r'^(?i)CaseInsensitive/(\w+)', empty_view, name="insensitive"),
url(r'^(?i)test/2/?$', empty_view, name="test2"),
]
@override_settings(ROOT_URLCONF='urlpatterns_reverse.test_deprecated')
class URLPatternReverse(SimpleTestCase):
def test_urlpattern_reverse(self):
test_data = (
('insensitive', '/CaseInsensitive/fred', ['fred'], {}),
('test2', '/test/2', [], {}),
)
with warnings.catch_warnings(record=True) as warns:
warnings.simplefilter('always')
warnings.filterwarnings(
'ignore', 'Flags not at the start',
DeprecationWarning, module='django.urls.resolvers'
)
for i, (name, expected, args, kwargs) in enumerate(test_data):
got = reverse(name, args=args, kwargs=kwargs)
self.assertEqual(got, expected)
msg = str(warns[i].message)
self.assertEqual(msg, 'Using (?i) in url() patterns is deprecated.')

View File

@ -198,9 +198,7 @@ test_data = (
('repeats', '/repeats/a/', [], {}),
('repeats2', '/repeats/aa/', [], {}),
('repeats3', '/repeats/aa/', [], {}),
('insensitive', '/CaseInsensitive/fred', ['fred'], {}),
('test', '/test/1', [], {}),
('test2', '/test/2', [], {}),
('inner-nothing', '/outer/42/', [], {'outer': '42'}),
('inner-nothing', '/outer/42/', ['42'], {}),
('inner-nothing', NoReverseMatch, ['foo'], {}),

View File

@ -49,9 +49,7 @@ urlpatterns = [
url(r'^repeats/a{1,2}/$', empty_view, name="repeats"),
url(r'^repeats/a{2,4}/$', empty_view, name="repeats2"),
url(r'^repeats/a{2}/$', empty_view, name="repeats3"),
url(r'^(?i)CaseInsensitive/(\w+)', empty_view, name="insensitive"),
url(r'^test/1/?', empty_view, name="test"),
url(r'^(?i)test/2/?$', empty_view, name="test2"),
url(r'^outer/(?P<outer>[0-9]+)/', include('urlpatterns_reverse.included_urls')),
url(r'^outer-no-kwargs/([0-9]+)/', include('urlpatterns_reverse.included_no_kwargs_urls')),
url('', include('urlpatterns_reverse.extra_urls')),

View File

@ -1,6 +1,7 @@
from __future__ import unicode_literals
import unittest
import warnings
from django.utils import regex_helper
@ -27,8 +28,12 @@ class NormalizeTests(unittest.TestCase):
def test_group_ignored(self):
pattern = r"(?i)(?L)(?m)(?s)(?u)(?#)"
expected = [('', [])]
result = regex_helper.normalize(pattern)
with warnings.catch_warnings(record=True) as warns:
warnings.simplefilter('always')
result = regex_helper.normalize(pattern)
self.assertEqual(result, expected)
for i, char in enumerate('iLmsu#'):
self.assertEqual(str(warns[i].message), 'Using (?%s) in url() patterns is deprecated.' % char)
def test_group_noncapturing(self):
pattern = r"(?:non-capturing)"