mirror of https://github.com/django/django.git
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
|
||||
|
||||
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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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/', [], {}),
|
||||
('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'], {}),
|
||||
|
|
|
@ -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')),
|
||||
|
|
|
@ -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)"
|
||||
|
|
Loading…
Reference in New Issue