Fixed #16362 -- Allowed lookaround assertions in URL patterns.
This commit is contained in:
parent
74f8110e74
commit
b4382b7055
|
@ -59,11 +59,10 @@ 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.
|
||||
(5) Ignore comments and any of the reg-exp flags that won't change
|
||||
what we construct ("iLmsu"). "(?x)" is an error, however.
|
||||
(6) Raise an error on all other non-capturing (?...) forms (e.g.
|
||||
look-ahead and look-behind matches) and any disjunctive ('|')
|
||||
constructs.
|
||||
(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.
|
||||
(5) Raise an error on any disjunctive ('|') constructs.
|
||||
|
||||
Django's URLs for forward resolving are either all positional arguments or
|
||||
all keyword arguments. That is assumed here, as well. Although reverse
|
||||
|
@ -129,7 +128,7 @@ def normalize(pattern):
|
|||
walk_to_end(ch, pattern_iter)
|
||||
else:
|
||||
ch, escaped = next(pattern_iter)
|
||||
if ch in "iLmsu#":
|
||||
if ch in "iLmsu#!=<":
|
||||
# All of these are ignorable. Walk to the end of the
|
||||
# group.
|
||||
walk_to_end(ch, pattern_iter)
|
||||
|
|
|
@ -205,6 +205,11 @@ Tests
|
|||
|
||||
* ...
|
||||
|
||||
URLs
|
||||
^^^^
|
||||
|
||||
* Regular expression lookaround assertions are now allowed in URL patterns.
|
||||
|
||||
Validators
|
||||
^^^^^^^^^^
|
||||
|
||||
|
|
|
@ -436,6 +436,7 @@ Login
|
|||
logout
|
||||
Logout
|
||||
Loïc
|
||||
lookaround
|
||||
lookup
|
||||
Lookup
|
||||
lookups
|
||||
|
|
|
@ -799,3 +799,50 @@ class IncludeTests(SimpleTestCase):
|
|||
msg = "Must specify a namespace if specifying app_name."
|
||||
with self.assertRaisesMessage(ValueError, msg):
|
||||
include('urls', app_name='bar')
|
||||
|
||||
|
||||
@override_settings(ROOT_URLCONF='urlpatterns_reverse.urls')
|
||||
class LookaheadTests(TestCase):
|
||||
def test_valid_resolve(self):
|
||||
test_urls = [
|
||||
'/lookahead-/a-city/',
|
||||
'/lookbehind-/a-city/',
|
||||
'/lookahead+/a-city/',
|
||||
'/lookbehind+/a-city/',
|
||||
]
|
||||
for test_url in test_urls:
|
||||
match = resolve(test_url)
|
||||
self.assertEqual(match.kwargs, {'city': 'a-city'})
|
||||
|
||||
def test_invalid_resolve(self):
|
||||
test_urls = [
|
||||
'/lookahead-/not-a-city/',
|
||||
'/lookbehind-/not-a-city/',
|
||||
'/lookahead+/other-city/',
|
||||
'/lookbehind+/other-city/',
|
||||
]
|
||||
for test_url in test_urls:
|
||||
with self.assertRaises(Resolver404):
|
||||
resolve(test_url)
|
||||
|
||||
def test_valid_reverse(self):
|
||||
url = reverse('lookahead-positive', kwargs={'city': 'a-city'})
|
||||
self.assertEqual(url, '/lookahead+/a-city/')
|
||||
url = reverse('lookahead-negative', kwargs={'city': 'a-city'})
|
||||
self.assertEqual(url, '/lookahead-/a-city/')
|
||||
|
||||
url = reverse('lookbehind-positive', kwargs={'city': 'a-city'})
|
||||
self.assertEqual(url, '/lookbehind+/a-city/')
|
||||
url = reverse('lookbehind-negative', kwargs={'city': 'a-city'})
|
||||
self.assertEqual(url, '/lookbehind-/a-city/')
|
||||
|
||||
def test_invalid_reverse(self):
|
||||
with self.assertRaises(NoReverseMatch):
|
||||
reverse('lookahead-positive', kwargs={'city': 'other-city'})
|
||||
with self.assertRaises(NoReverseMatch):
|
||||
reverse('lookahead-negative', kwargs={'city': 'not-a-city'})
|
||||
|
||||
with self.assertRaises(NoReverseMatch):
|
||||
reverse('lookbehind-positive', kwargs={'city': 'other-city'})
|
||||
with self.assertRaises(NoReverseMatch):
|
||||
reverse('lookbehind-negative', kwargs={'city': 'not-a-city'})
|
||||
|
|
|
@ -62,6 +62,10 @@ with warnings.catch_warnings():
|
|||
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')),
|
||||
url(r'^lookahead-/(?!not-a-city)(?P<city>[^/]+)/$', empty_view, name='lookahead-negative'),
|
||||
url(r'^lookahead\+/(?=a-city)(?P<city>[^/]+)/$', empty_view, name='lookahead-positive'),
|
||||
url(r'^lookbehind-/(?P<city>[^/]+)(?<!not-a-city)/$', empty_view, name='lookbehind-negative'),
|
||||
url(r'^lookbehind\+/(?P<city>[^/]+)(?<=a-city)/$', empty_view, name='lookbehind-positive'),
|
||||
|
||||
# Partials should be fine.
|
||||
url(r'^partial/', empty_view_partial, name="partial"),
|
||||
|
|
Loading…
Reference in New Issue