From c971a7da2c5924286eb5f94aa8b3d23bffe3b154 Mon Sep 17 00:00:00 2001 From: Ben Darnell Date: Fri, 22 Jul 2016 14:28:52 -0400 Subject: [PATCH] web: URLs that cannot be unescaped should be allowed Fixes a regression in version 4.4 Fixes #1770 --- tornado/test/web_test.py | 23 +++++++++++++++++++++++ tornado/web.py | 8 +++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py index 745d0fa84..fdd1797cc 100644 --- a/tornado/test/web_test.py +++ b/tornado/test/web_test.py @@ -2811,3 +2811,26 @@ class ApplicationTest(AsyncTestCase): class URLSpecReverseTest(unittest.TestCase): def test_reverse(self): self.assertEqual('/favicon.ico', url(r'/favicon\.ico', None).reverse()) + self.assertEqual('/favicon.ico', url(r'^/favicon\.ico$', None).reverse()) + + def test_non_reversible(self): + # URLSpecs are non-reversible if they include non-constant + # regex features outside capturing groups. Currently, this is + # only strictly enforced for backslash-escaped character + # classes. + paths = [ + r'^/api/v\d+/foo/(\w+)$', + ] + for path in paths: + # A URLSpec can still be created even if it cannot be reversed. + url_spec = url(path, None) + try: + result = url_spec.reverse() + self.fail("did not get expected exception when reversing %s. " + "result: %s" % (path, result)) + except ValueError: + pass + + def test_reverse_arguments(self): + self.assertEqual('/api/v1/foo/bar', + url(r'^/api/v1/foo/(\w+)$', None).reverse('bar')) diff --git a/tornado/web.py b/tornado/web.py index 479dd2457..f54c4d039 100644 --- a/tornado/web.py +++ b/tornado/web.py @@ -3059,14 +3059,16 @@ class URLSpec(object): try: unescaped_fragment = re_unescape(fragment) except ValueError as exc: - raise ValueError(exc.args[0] + '; invalid url: %r' % pattern) + # If we can't unescape part of it, we can't + # reverse this url. + return (None, None) pieces.append(unescaped_fragment) return (''.join(pieces), self.regex.groups) def reverse(self, *args): - assert self._path is not None, \ - "Cannot reverse url regex " + self.regex.pattern + if self._path is None: + raise ValueError("Cannot reverse url regex " + self.regex.pattern) assert len(args) == self._group_count, "required number of arguments "\ "not found" if not len(args): -- 2.47.2