Fixed #27648 -- Deprecated (iLmsu) regex groups in url() patterns.
This commit is contained in:
parent
544b2ef29f
commit
51cde873d9
|
@ -7,7 +7,10 @@ should be good enough for a large class of URLS, however.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import warnings
|
||||||
|
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
from django.utils.deprecation import RemovedInDjango21Warning
|
||||||
from django.utils.six.moves import zip
|
from django.utils.six.moves import zip
|
||||||
|
|
||||||
# Mapping of an escape character to a representative of that class. So, e.g.,
|
# 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
|
(3) Select the first (essentially an arbitrary) element from any character
|
||||||
class. Select an arbitrary character for any unordered class (e.g. '.'
|
class. Select an arbitrary character for any unordered class (e.g. '.'
|
||||||
or '\w') in the pattern.
|
or '\w') in the pattern.
|
||||||
(4) Ignore comments, look-ahead and look-behind assertions, and any of the
|
(4) Ignore look-ahead and look-behind assertions.
|
||||||
reg-exp flags that won't change what we construct ("iLmsu"). "(?x)" is
|
|
||||||
an error, however.
|
|
||||||
(5) Raise an error on any disjunctive ('|') constructs.
|
(5) Raise an error on any disjunctive ('|') constructs.
|
||||||
|
|
||||||
Django's URLs for forward resolving are either all positional arguments or
|
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)
|
walk_to_end(ch, pattern_iter)
|
||||||
else:
|
else:
|
||||||
ch, escaped = next(pattern_iter)
|
ch, escaped = next(pattern_iter)
|
||||||
if ch in "iLmsu#!=<":
|
if ch in '!=<':
|
||||||
# All of these are ignorable. Walk to the end of the
|
# All of these are ignorable. Walk to the end of the
|
||||||
# group.
|
# group.
|
||||||
walk_to_end(ch, pattern_iter)
|
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 == ':':
|
elif ch == ':':
|
||||||
# Non-capturing group
|
# Non-capturing group
|
||||||
non_capturing_groups.append(len(result))
|
non_capturing_groups.append(len(result))
|
||||||
|
|
|
@ -48,6 +48,9 @@ details on these changes.
|
||||||
|
|
||||||
* The ``Model._meta.has_auto_field`` attribute will be removed.
|
* 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:
|
.. _deprecation-removed-in-2.0:
|
||||||
|
|
||||||
2.0
|
2.0
|
||||||
|
|
|
@ -747,3 +747,10 @@ Miscellaneous
|
||||||
|
|
||||||
* ``Model._meta.has_auto_field`` is deprecated in favor of checking if
|
* ``Model._meta.has_auto_field`` is deprecated in favor of checking if
|
||||||
``Model._meta.auto_field is not None``.
|
``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.
|
||||||
|
|
|
@ -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.')
|
|
@ -198,9 +198,7 @@ test_data = (
|
||||||
('repeats', '/repeats/a/', [], {}),
|
('repeats', '/repeats/a/', [], {}),
|
||||||
('repeats2', '/repeats/aa/', [], {}),
|
('repeats2', '/repeats/aa/', [], {}),
|
||||||
('repeats3', '/repeats/aa/', [], {}),
|
('repeats3', '/repeats/aa/', [], {}),
|
||||||
('insensitive', '/CaseInsensitive/fred', ['fred'], {}),
|
|
||||||
('test', '/test/1', [], {}),
|
('test', '/test/1', [], {}),
|
||||||
('test2', '/test/2', [], {}),
|
|
||||||
('inner-nothing', '/outer/42/', [], {'outer': '42'}),
|
('inner-nothing', '/outer/42/', [], {'outer': '42'}),
|
||||||
('inner-nothing', '/outer/42/', ['42'], {}),
|
('inner-nothing', '/outer/42/', ['42'], {}),
|
||||||
('inner-nothing', NoReverseMatch, ['foo'], {}),
|
('inner-nothing', NoReverseMatch, ['foo'], {}),
|
||||||
|
|
|
@ -49,9 +49,7 @@ urlpatterns = [
|
||||||
url(r'^repeats/a{1,2}/$', empty_view, name="repeats"),
|
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,4}/$', empty_view, name="repeats2"),
|
||||||
url(r'^repeats/a{2}/$', empty_view, name="repeats3"),
|
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'^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/(?P<outer>[0-9]+)/', include('urlpatterns_reverse.included_urls')),
|
||||||
url(r'^outer-no-kwargs/([0-9]+)/', include('urlpatterns_reverse.included_no_kwargs_urls')),
|
url(r'^outer-no-kwargs/([0-9]+)/', include('urlpatterns_reverse.included_no_kwargs_urls')),
|
||||||
url('', include('urlpatterns_reverse.extra_urls')),
|
url('', include('urlpatterns_reverse.extra_urls')),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
import warnings
|
||||||
|
|
||||||
from django.utils import regex_helper
|
from django.utils import regex_helper
|
||||||
|
|
||||||
|
@ -27,8 +28,12 @@ class NormalizeTests(unittest.TestCase):
|
||||||
def test_group_ignored(self):
|
def test_group_ignored(self):
|
||||||
pattern = r"(?i)(?L)(?m)(?s)(?u)(?#)"
|
pattern = r"(?i)(?L)(?m)(?s)(?u)(?#)"
|
||||||
expected = [('', [])]
|
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)
|
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):
|
def test_group_noncapturing(self):
|
||||||
pattern = r"(?:non-capturing)"
|
pattern = r"(?:non-capturing)"
|
||||||
|
|
Loading…
Reference in New Issue