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:
committed by
Kurt Griffiths
parent
e10ff51027
commit
efa763042f
@@ -65,7 +65,9 @@ def compile_uri_template(template):
|
|||||||
if template != '/' and template.endswith('/'):
|
if template != '/' and template.endswith('/'):
|
||||||
template = template[:-1]
|
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
|
# Get a list of field names
|
||||||
fields = set(re.findall(expression_pattern, template))
|
fields = set(re.findall(expression_pattern, template))
|
||||||
|
|||||||
@@ -27,6 +27,18 @@ class NameResource(object):
|
|||||||
self.called = True
|
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):
|
class TestUriTemplates(testing.TestBase):
|
||||||
|
|
||||||
def before(self):
|
def before(self):
|
||||||
@@ -116,6 +128,20 @@ class TestUriTemplates(testing.TestBase):
|
|||||||
self.assertNotIn(kwargs, 'Id')
|
self.assertNotIn(kwargs, 'Id')
|
||||||
self.assertEqual(req.get_param('id'), None)
|
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):
|
def test_single_trailing_slash(self):
|
||||||
resource1 = IDResource()
|
resource1 = IDResource()
|
||||||
self.api.add_route('/1/{id}/', resource1)
|
self.api.add_route('/1/{id}/', resource1)
|
||||||
@@ -158,6 +184,19 @@ class TestUriTemplates(testing.TestBase):
|
|||||||
self.assertEqual(resource.id, test_id)
|
self.assertEqual(resource.id, test_id)
|
||||||
self.assertEqual(resource.name, test_name)
|
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):
|
def test_empty_path_component(self):
|
||||||
self.assertRaises(ValueError, self.api.add_route,
|
self.assertRaises(ValueError, self.api.add_route,
|
||||||
'//', self.resource)
|
'//', self.resource)
|
||||||
|
|||||||
@@ -69,15 +69,30 @@ class TestUriTemplates(testing.TestBase):
|
|||||||
self.assertTrue(result)
|
self.assertTrue(result)
|
||||||
self.assertEqual(result.groupdict(), {'name': 'Kelsier'})
|
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('', '/')
|
@ddt.data('', '/')
|
||||||
def test_two_fields(self, postfix):
|
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)
|
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')
|
result = pattern.match('/book/0765350386/characters/Vin')
|
||||||
self.assertTrue(result)
|
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):
|
def test_three_fields(self):
|
||||||
fields, pattern = routing.compile_uri_template('/{a}/{b}/x/{c}')
|
fields, pattern = routing.compile_uri_template('/{a}/{b}/x/{c}')
|
||||||
|
|||||||
Reference in New Issue
Block a user