diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index 2400fddcfc2..732220afe42 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -10,6 +10,7 @@ from collections.abc import Sized, Iterable, Container from urllib.parse import urlencode, unquote +from types import MappingProxyType from . import hdrs from .abc import AbstractRouter, AbstractMatchInfo @@ -440,6 +441,9 @@ def __getitem__(self, name): def routes(self): return RoutesView(self._urls) + def named_routes(self): + return MappingProxyType(self._routes) + def register_route(self, route): assert isinstance(route, Route), 'Instance of Route class is required.' diff --git a/docs/web_reference.rst b/docs/web_reference.rst index bb82a1e4025..878f497ae8f 100644 --- a/docs/web_reference.rst +++ b/docs/web_reference.rst @@ -1210,6 +1210,27 @@ Router is any object that implements :class:`AbstractRouter` interface. .. versionadded:: 0.18 + .. method:: named_routes() + + Returns a :obj:`dict`-like :class:`types.MappingProxyType` *view* over + *all* named routes. + + The view maps every named route's :attr:`Route.name` attribute to the + :class:`Route`. It supports the usual :obj:`dict`-like operations, except + for any mutable operations (i.e. it's **read-only**):: + + len(app.router.named_routes()) + + for name, route in app.router.named_routes().items(): + print(name, route) + + "route_name" in app.router.named_routes() + + app.router.named_routes()["route_name"] + + .. versionadded:: 0.19 + + .. _aiohttp-web-route: Route diff --git a/tests/test_urldispatch.py b/tests/test_urldispatch.py index 72f744407bb..354002c08fa 100644 --- a/tests/test_urldispatch.py +++ b/tests/test_urldispatch.py @@ -1,7 +1,7 @@ import asyncio import os import unittest -from collections.abc import Sized, Container, Iterable +from collections.abc import Sized, Container, Iterable, Mapping, MutableMapping from unittest import mock from urllib.parse import unquote import aiohttp.web @@ -642,3 +642,26 @@ def test_routes_abc(self): self.assertIsInstance(self.router.routes(), Sized) self.assertIsInstance(self.router.routes(), Iterable) self.assertIsInstance(self.router.routes(), Container) + + def fill_named_routes(self): + route1 = self.router.add_route('GET', '/plain', self.make_handler(), + name='route1') + route2 = self.router.add_route('GET', '/variable/{name}', + self.make_handler(), name='route2') + route3 = self.router.add_static('/static', + os.path.dirname(aiohttp.__file__), + name='route3') + return route1, route2, route3 + + def test_named_routes_abc(self): + self.assertIsInstance(self.router.named_routes(), Mapping) + self.assertNotIsInstance(self.router.named_routes(), MutableMapping) + + def test_named_routes(self): + named_routes = self.fill_named_routes() + + self.assertEqual(3, len(self.router.named_routes())) + + for route in named_routes: + self.assertIn(route.name, self.router.named_routes()) + self.assertEqual(route, self.router.named_routes()[route.name])