Allow nested routes sharing the same path parameter matcher
When two routes share the same parameter at the same node position
(e.g. GET /restaurants/${id} and GET /restaurants/${id}/menu),
reuse the existing equivalent matcher instead of raising a conflict.
Non-equivalent matchers (different names, kinds, or glob patterns)
at the same node for the same method still raise ValueError.
This commit is contained in:
@@ -84,7 +84,14 @@ class TreeTest(unittest.TestCase):
|
||||
tree = Tree()
|
||||
tree.add((p for p in ('foo', '*')), None, self.handlers[0])
|
||||
with self.assertRaises(ValueError):
|
||||
tree.add((p for p in ('foo', '*')), None, self.handlers[1])
|
||||
tree.add((p for p in ('foo', '*.md')), None, self.handlers[1])
|
||||
|
||||
def test_identical_method_agnostic_matchers_reuse(self):
|
||||
tree = Tree()
|
||||
tree.add((p for p in ('foo', '*')), None, self.handlers[0])
|
||||
tree.add((p for p in ('foo', '*')), None, self.handlers[1])
|
||||
handler = Maybe.of_nullable(tree.get_handler('/foo/bar', HttpMethod.GET)).map(lambda it: it[0]).or_none()
|
||||
self.assertIs(self.handlers[0], handler)
|
||||
|
||||
def test_two_overlapping_method_specific_matchers_raise(self):
|
||||
tree = Tree()
|
||||
@@ -101,3 +108,51 @@ class TreeTest(unittest.TestCase):
|
||||
self.assertIs(self.handlers[0], put_handler)
|
||||
self.assertIs(self.handlers[1], get_handler)
|
||||
|
||||
def test_nested_routes_with_same_param_allowed(self):
|
||||
tree = Tree()
|
||||
tree.add((p for p in ('restaurants', '${id}')), HttpMethod.GET, self.handlers[0])
|
||||
tree.add((p for p in ('restaurants', '${id}', 'menu')), HttpMethod.GET, self.handlers[1])
|
||||
h0 = Maybe.of_nullable(tree.get_handler('/restaurants/42', HttpMethod.GET)).map(lambda it: it[0]).or_none()
|
||||
h1 = Maybe.of_nullable(tree.get_handler('/restaurants/42/menu', HttpMethod.GET)).map(lambda it: it[0]).or_none()
|
||||
self.assertIs(self.handlers[0], h0)
|
||||
self.assertIs(self.handlers[1], h1)
|
||||
|
||||
def test_nested_routes_same_param_reverse_order(self):
|
||||
tree = Tree()
|
||||
tree.add((p for p in ('restaurants', '${id}', 'menu')), HttpMethod.GET, self.handlers[1])
|
||||
tree.add((p for p in ('restaurants', '${id}')), HttpMethod.GET, self.handlers[0])
|
||||
h0 = Maybe.of_nullable(tree.get_handler('/restaurants/42', HttpMethod.GET)).map(lambda it: it[0]).or_none()
|
||||
h1 = Maybe.of_nullable(tree.get_handler('/restaurants/42/menu', HttpMethod.GET)).map(lambda it: it[0]).or_none()
|
||||
self.assertIs(self.handlers[0], h0)
|
||||
self.assertIs(self.handlers[1], h1)
|
||||
|
||||
def test_nested_routes_with_same_int_param_allowed(self):
|
||||
tree = Tree()
|
||||
tree.add((p for p in ('restaurants', '${id:int}')), HttpMethod.GET, self.handlers[0])
|
||||
tree.add((p for p in ('restaurants', '${id:int}', 'menu')), HttpMethod.GET, self.handlers[1])
|
||||
h0 = Maybe.of_nullable(tree.get_handler('/restaurants/42', HttpMethod.GET)).map(lambda it: it[0]).or_none()
|
||||
h1 = Maybe.of_nullable(tree.get_handler('/restaurants/42/menu', HttpMethod.GET)).map(lambda it: it[0]).or_none()
|
||||
self.assertIs(self.handlers[0], h0)
|
||||
self.assertIs(self.handlers[1], h1)
|
||||
|
||||
def test_nested_routes_method_agnostic_reuses_matcher(self):
|
||||
tree = Tree()
|
||||
tree.add((p for p in ('restaurants', '${id}')), HttpMethod.GET, self.handlers[0])
|
||||
tree.add((p for p in ('restaurants', '${id}', 'menu')), None, self.handlers[1])
|
||||
h0 = Maybe.of_nullable(tree.get_handler('/restaurants/42', HttpMethod.GET)).map(lambda it: it[0]).or_none()
|
||||
h1 = Maybe.of_nullable(tree.get_handler('/restaurants/42/menu', HttpMethod.POST)).map(lambda it: it[0]).or_none()
|
||||
self.assertIs(self.handlers[0], h0)
|
||||
self.assertIs(self.handlers[1], h1)
|
||||
|
||||
def test_different_param_names_still_raise(self):
|
||||
tree = Tree()
|
||||
tree.add((p for p in ('a', '${id}')), HttpMethod.GET, self.handlers[0])
|
||||
with self.assertRaises(ValueError):
|
||||
tree.add((p for p in ('a', '${name}', 'x')), HttpMethod.GET, self.handlers[1])
|
||||
|
||||
def test_different_param_kinds_still_raise(self):
|
||||
tree = Tree()
|
||||
tree.add((p for p in ('a', '${id}')), HttpMethod.GET, self.handlers[0])
|
||||
with self.assertRaises(ValueError):
|
||||
tree.add((p for p in ('a', '${id:int}', 'x')), HttpMethod.GET, self.handlers[1])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user