diff --git a/CHANGES/2586.feature b/CHANGES/2586.feature new file mode 100644 index 00000000000..c5082930050 --- /dev/null +++ b/CHANGES/2586.feature @@ -0,0 +1,2 @@ +Avoid to create a new resource when adding a route with the same +name and path of the last added resource \ No newline at end of file diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index ad5d7ecf616..bfc84ff5e84 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -299,6 +299,10 @@ async def resolve(self, request): else: return None, allowed_methods + @abc.abstractmethod + def raw_match(self, path): + """Perform a raw match against path""" + def __len__(self): return len(self._routes) @@ -332,6 +336,9 @@ def _match(self, path): else: return None + def raw_match(self, path): + return self._path == path + def get_info(self): return {'path': self._path} @@ -400,6 +407,9 @@ def _match(self, path): return {key: unquote(value, unsafe='+') for key, value in match.groupdict().items()} + def raw_match(self, path): + return self._formatter == path + def get_info(self): return {'formatter': self._formatter, 'pattern': self._pattern} @@ -830,6 +840,11 @@ def register_resource(self, resource): def add_resource(self, path, *, name=None): if path and not path.startswith('/'): raise ValueError("path should be started with / or be empty") + # Reuse last added resource if path and name are the same + if self._resources: + resource = self._resources[-1] + if resource.name == name and resource.raw_match(path): + return resource if not ('{' in path or '}' in path or ROUTE_RE.search(path)): url = URL(path) resource = PlainResource(url.raw_path, name=name) diff --git a/tests/test_web_urldispatcher.py b/tests/test_web_urldispatcher.py index e11e786531d..d4f9393f483 100644 --- a/tests/test_web_urldispatcher.py +++ b/tests/test_web_urldispatcher.py @@ -342,3 +342,23 @@ async def handler(_): r = await client.head('/b') assert r.status == 405 await r.release() + + +@pytest.mark.parametrize("path", [ + '/a', + '/{a}', +]) +def test_reuse_last_added_resource(path): + """ + Test that adding a route with the same name and path of the last added + resource doesn't create a new resource. + """ + app = web.Application() + + async def handler(request): + return web.Response() + + app.router.add_get(path, handler, name="a") + app.router.add_post(path, handler, name="a") + + assert len(app.router.resources()) == 1