From 9595236d4f1212323dbf06670a80e6a4fb7c8fcf Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 9 Feb 2011 02:32:29 -0500 Subject: - URL pattern markers used in URL dispatch are permitted to specify a custom regex. For example, the pattern ``/{foo:\d+}`` means to match ``/12345`` (foo==12345 in the match dictionary) but not ``/abc``. However, custom regexes in a pattern marker which used squiggly brackets did not work. For example, ``/{foo:\d{4}}`` would fail to match ``/1234`` and ``/{foo:\d{1,2}}`` would fail to match ``/1`` or ``/11``. One level of inner squiggly brackets is now recognized so that the prior two patterns given as examples now work. See also https://github.com/Pylons/pyramid/issues/#issue/123. Closes #123 --- CHANGES.txt | 16 ++++++++++++++++ pyramid/tests/test_urldispatch.py | 16 +++++++++++++++- pyramid/urldispatch.py | 10 +++++++++- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ffafe1b7d..34e8cd7b6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,19 @@ +Next release +============ + +Bug Fixes +--------- + +- URL pattern markers used in URL dispatch are permitted to specify a custom + regex. For example, the pattern ``/{foo:\d+}`` means to match ``/12345`` + (foo==12345 in the match dictionary) but not ``/abc``. However, custom + regexes in a pattern marker which used squiggly brackets did not work. For + example, ``/{foo:\d{4}}`` would fail to match ``/1234`` and + ``/{foo:\d{1,2}}`` would fail to match ``/1`` or ``/11``. One level of + inner squiggly brackets is now recognized so that the prior two patterns + given as examples now work. See also + https://github.com/Pylons/pyramid/issues/#issue/123. + 1.0 (2011-01-30) ================ diff --git a/pyramid/tests/test_urldispatch.py b/pyramid/tests/test_urldispatch.py index a452fdb97..eca4603a3 100644 --- a/pyramid/tests/test_urldispatch.py +++ b/pyramid/tests/test_urldispatch.py @@ -268,7 +268,21 @@ class TestCompileRoute(unittest.TestCase): self.assertEqual(matcher('/foo/baz/biz/buz.bar'), {'baz':'baz', 'buz':'buz', 'bar':'bar'}) self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) - self.assertEqual(generator({'baz':1, 'buz':2, 'bar': 'html'}), '/foo/1/biz/2.html') + self.assertEqual(generator({'baz':1, 'buz':2, 'bar': 'html'}), + '/foo/1/biz/2.html') + + def test_custom_regex_with_embedded_squigglies(self): + matcher, generator = self._callFUT('/{buz:\d{4}}') + self.assertEqual(matcher('/2001'), {'buz':'2001'}) + self.assertEqual(matcher('/200'), None) + self.assertEqual(generator({'buz':2001}), '/2001') + + def test_custom_regex_with_embedded_squigglies2(self): + matcher, generator = self._callFUT('/{buz:\d{3,4}}') + self.assertEqual(matcher('/2001'), {'buz':'2001'}) + self.assertEqual(matcher('/200'), {'buz':'200'}) + self.assertEqual(matcher('/20'), None) + self.assertEqual(generator({'buz':2001}), '/2001') class TestCompileRouteMatchFunctional(unittest.TestCase): def matches(self, pattern, path, expected): diff --git a/pyramid/urldispatch.py b/pyramid/urldispatch.py index 5d1e53947..57ca8ff83 100644 --- a/pyramid/urldispatch.py +++ b/pyramid/urldispatch.py @@ -76,7 +76,13 @@ class RoutesMapper(object): # stolen from bobo and modified old_route_re = re.compile(r'(\:[a-zA-Z]\w*)') star_in_brackets = re.compile(r'\{[^\}]*\*\w*[^\}]*\}') -route_re = re.compile(r'(\{[a-zA-Z][^\}]*\})') +# The torturous nature of the regex named ``route_re`` below is due to the +# fact that we need to support at least one level of "inner" squigglies +# inside the expr of a {name:expr} pattern. This regex used to be just +# (\{[a-zA-Z][^\}]*\}) but that choked when supplied with e.g. {foo:\d{4}}. +# Thanks to Zart for the regex help. +route_re = re.compile(r'(\{[a-zA-Z](?:\{[^\}]*\}|[^\{\}]*)*\})') + def update_pattern(matchobj): name = matchobj.group(0) return '{%s}' % name[1:] @@ -103,6 +109,8 @@ def _compile_route(route): name = name[1:-1] if ':' in name: name, reg = name.split(':') + reg = reg.replace(r'\{', '{') + reg = reg.replace(r'\}', '}') else: reg = '[^/]+' gen.append('%%(%s)s' % name) -- cgit v1.2.3