feat(routing): Allow digits and underscore in compile_uri_template

Allow URI template fields to contain numbers so long as the field name
does not start with a numeral. Example: "test123" is valid, while
"123test" is not.

This patch adds support for this in the legacy template helpers, and also
adds an additional test for these chars for the current router spec.

Co-Authored-By: Kurt Griffiths <mail@kgriffs.com>
This commit is contained in:
BenjamenMeyer
2015-02-19 19:14:13 +00:00
committed by Kurt Griffiths
parent e10ff51027
commit efa763042f
3 changed files with 60 additions and 4 deletions

View File

@@ -65,7 +65,9 @@ def compile_uri_template(template):
if template != '/' and template.endswith('/'):
template = template[:-1]
expression_pattern = r'{([a-zA-Z][a-zA-Z_]*)}'
# template names should be able to start with A-Za-z
# but also contain 0-9_ in the remaining portion
expression_pattern = r'{([a-zA-Z]\w*)}'
# Get a list of field names
fields = set(re.findall(expression_pattern, template))

View File

@@ -27,6 +27,18 @@ class NameResource(object):
self.called = True
class NameAndDigitResource(object):
def __init__(self):
self.id = None
self.name51 = None
self.called = False
def on_get(self, req, resp, id, name51):
self.id = id
self.name51 = name51
self.called = True
class TestUriTemplates(testing.TestBase):
def before(self):
@@ -116,6 +128,20 @@ class TestUriTemplates(testing.TestBase):
self.assertNotIn(kwargs, 'Id')
self.assertEqual(req.get_param('id'), None)
def test_single_with_trailing_digits(self):
self.api.add_route('/widgets/{id12}', self.resource)
self.simulate_request('/widgets/123')
self.assertTrue(self.resource.called)
self.assertEqual(self.resource.kwargs['id12'], '123')
def test_single_with_underscore(self):
self.api.add_route('/widgets/{widget_id}', self.resource)
self.simulate_request('/widgets/123')
self.assertTrue(self.resource.called)
self.assertEqual(self.resource.kwargs['widget_id'], '123')
def test_single_trailing_slash(self):
resource1 = IDResource()
self.api.add_route('/1/{id}/', resource1)
@@ -158,6 +184,19 @@ class TestUriTemplates(testing.TestBase):
self.assertEqual(resource.id, test_id)
self.assertEqual(resource.name, test_name)
def test_multiple_with_digits(self):
resource = NameAndDigitResource()
self.api.add_route('/messages/{id}/names/{name51}', resource)
test_id = self.getUniqueString()
test_name = self.getUniqueString()
path = '/messages/' + test_id + '/names/' + test_name
self.simulate_request(path)
self.assertTrue(resource.called)
self.assertEqual(resource.id, test_id)
self.assertEqual(resource.name51, test_name)
def test_empty_path_component(self):
self.assertRaises(ValueError, self.api.add_route,
'//', self.resource)

View File

@@ -69,15 +69,30 @@ class TestUriTemplates(testing.TestBase):
self.assertTrue(result)
self.assertEqual(result.groupdict(), {'name': 'Kelsier'})
def test_one_field_with_digits(self):
fields, pattern = routing.compile_uri_template('/{name123}')
self.assertEqual(fields, set(['name123']))
result = pattern.match('/Kelsier')
self.assertTrue(result)
self.assertEqual(result.groupdict(), {'name123': 'Kelsier'})
def test_one_field_with_prefixed_digits(self):
fields, pattern = routing.compile_uri_template('/{37signals}')
self.assertEqual(fields, set())
result = pattern.match('/s2n')
self.assertFalse(result)
@ddt.data('', '/')
def test_two_fields(self, postfix):
path = '/book/{id}/characters/{name}' + postfix
path = '/book/{book_id}/characters/{n4m3}' + postfix
fields, pattern = routing.compile_uri_template(path)
self.assertEqual(fields, set(['name', 'id']))
self.assertEqual(fields, set(['n4m3', 'book_id']))
result = pattern.match('/book/0765350386/characters/Vin')
self.assertTrue(result)
self.assertEqual(result.groupdict(), {'name': 'Vin', 'id': '0765350386'})
self.assertEqual(result.groupdict(), {'n4m3': 'Vin', 'book_id': '0765350386'})
def test_three_fields(self):
fields, pattern = routing.compile_uri_template('/{a}/{b}/x/{c}')