From e7e5946c2ec65fd9e50053ed427cc7b938917785 Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Thu, 11 Jun 2015 00:32:05 +0200 Subject: [PATCH 1/2] `format_suffix_patterns` is applied before the trailing slash. --- rest_framework/urlpatterns.py | 6 ++--- tests/test_urlpatterns.py | 50 ++++++++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/rest_framework/urlpatterns.py b/rest_framework/urlpatterns.py index 038e9ee38..2e4369f71 100644 --- a/rest_framework/urlpatterns.py +++ b/rest_framework/urlpatterns.py @@ -21,7 +21,7 @@ def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required): else: # Regular URL pattern - regex = urlpattern.regex.pattern.rstrip('$') + suffix_pattern + regex = urlpattern.regex.pattern.rstrip('$').rstrip('/') + suffix_pattern view = urlpattern._callback or urlpattern._callback_str kwargs = urlpattern.default_args name = urlpattern.name @@ -55,8 +55,8 @@ def format_suffix_patterns(urlpatterns, suffix_required=False, allowed=None): allowed_pattern = allowed[0] else: allowed_pattern = '(%s)' % '|'.join(allowed) - suffix_pattern = r'\.(?P<%s>%s)$' % (suffix_kwarg, allowed_pattern) + suffix_pattern = r'\.(?P<%s>%s)/?$' % (suffix_kwarg, allowed_pattern) else: - suffix_pattern = r'\.(?P<%s>[a-z0-9]+)$' % suffix_kwarg + suffix_pattern = r'\.(?P<%s>[a-z0-9]+)/?$' % suffix_kwarg return apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required) diff --git a/tests/test_urlpatterns.py b/tests/test_urlpatterns.py index e0060e690..898acd352 100644 --- a/tests/test_urlpatterns.py +++ b/tests/test_urlpatterns.py @@ -17,7 +17,8 @@ def dummy_view(request, *args, **kwargs): class FormatSuffixTests(TestCase): """ - Tests `format_suffix_patterns` against different URLPatterns to ensure the URLs still resolve properly, including any captured parameters. + Tests `format_suffix_patterns` against different URLPatterns to ensure the + URLs still resolve properly, including any captured parameters. """ def _resolve_urlpatterns(self, urlpatterns, test_paths): factory = APIRequestFactory() @@ -35,11 +36,37 @@ class FormatSuffixTests(TestCase): self.assertEqual(callback_args, test_path.args) self.assertEqual(callback_kwargs, test_path.kwargs) + def test_trailing_slash(self): + factory = APIRequestFactory() + urlpatterns = format_suffix_patterns([ + url(r'^test/$', dummy_view), + ]) + resolver = urlresolvers.RegexURLResolver(r'^/', urlpatterns) + + test_paths = [ + (URLTestPath('/test.api', (), {'format': 'api'}), True), + (URLTestPath('/test/.api', (), {'format': 'api'}), False), + (URLTestPath('/test.api/', (), {'format': 'api'}), True), + ] + + for test_path, expected_resolved in test_paths: + request = factory.get(test_path.path) + try: + callback, callback_args, callback_kwargs = resolver.resolve(request.path_info) + except urlresolvers.Resolver404: + callback, callback_args, callback_kwargs = (None, None, None) + if not expected_resolved: + assert callback is None + continue + + print(test_path, callback, callback_args, callback_kwargs) + assert callback_args == test_path.args + assert callback_kwargs == test_path.kwargs + def test_format_suffix(self): - urlpatterns = patterns( - '', + urlpatterns = [ url(r'^test$', dummy_view), - ) + ] test_paths = [ URLTestPath('/test', (), {}), URLTestPath('/test.api', (), {'format': 'api'}), @@ -48,10 +75,9 @@ class FormatSuffixTests(TestCase): self._resolve_urlpatterns(urlpatterns, test_paths) def test_default_args(self): - urlpatterns = patterns( - '', + urlpatterns = [ url(r'^test$', dummy_view, {'foo': 'bar'}), - ) + ] test_paths = [ URLTestPath('/test', (), {'foo': 'bar', }), URLTestPath('/test.api', (), {'foo': 'bar', 'format': 'api'}), @@ -60,14 +86,12 @@ class FormatSuffixTests(TestCase): self._resolve_urlpatterns(urlpatterns, test_paths) def test_included_urls(self): - nested_patterns = patterns( - '', + nested_patterns = [ url(r'^path$', dummy_view) - ) - urlpatterns = patterns( - '', + ] + urlpatterns = [ url(r'^test/', include(nested_patterns), {'foo': 'bar'}), - ) + ] test_paths = [ URLTestPath('/test/path', (), {'foo': 'bar', }), URLTestPath('/test/path.api', (), {'foo': 'bar', 'format': 'api'}), From fb2c09f6ae0d1b2d44f4fb8444fbbb80e69b6a48 Mon Sep 17 00:00:00 2001 From: Xavier Ordoquy Date: Thu, 11 Jun 2015 00:36:37 +0200 Subject: [PATCH 2/2] Remove unused patterns import. --- tests/test_urlpatterns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_urlpatterns.py b/tests/test_urlpatterns.py index 898acd352..f58388c52 100644 --- a/tests/test_urlpatterns.py +++ b/tests/test_urlpatterns.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals from collections import namedtuple -from django.conf.urls import patterns, url, include +from django.conf.urls import url, include from django.core import urlresolvers from django.test import TestCase from rest_framework.test import APIRequestFactory