diff --git a/CHANGES/3866.feature b/CHANGES/3866.feature new file mode 100644 index 00000000000..ede67ac7420 --- /dev/null +++ b/CHANGES/3866.feature @@ -0,0 +1,3 @@ +`web.UrlDispatcher.add_routes` and `web.Application.add_routes` return a list +of registered `AbstractRoute` instances. `AbstractRouteDef.register` (and all +subclasses) return a list of registered resources registered resource. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index a918fd456a2..dc37a531dee 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -271,5 +271,6 @@ Young-Ho Cha Yuriy Shatrov Yury Selivanov Yusuke Tsutsumi +Zlatan Sičanica Марк Коренберг Семён Марьясин diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py index 783bdbe9dc2..fb197f19ac4 100644 --- a/aiohttp/web_app.py +++ b/aiohttp/web_app.py @@ -44,6 +44,7 @@ from .web_server import Server from .web_urldispatcher import ( AbstractResource, + AbstractRoute, Domain, MaskDomain, MatchedSubAppResource, @@ -310,8 +311,9 @@ def add_domain(self, domain: str, factory = partial(MatchedSubAppResource, rule, subapp) return self._add_subapp(factory, subapp) - def add_routes(self, routes: Iterable[AbstractRouteDef]) -> None: - self.router.add_routes(routes) + def add_routes(self, + routes: Iterable[AbstractRouteDef]) -> List[AbstractRoute]: + return self.router.add_routes(routes) @property def on_response_prepare(self) -> _RespPrepareSignal: diff --git a/aiohttp/web_routedef.py b/aiohttp/web_routedef.py index 91b5ef0fee8..7fbc848d702 100644 --- a/aiohttp/web_routedef.py +++ b/aiohttp/web_routedef.py @@ -22,11 +22,14 @@ from .typedefs import PathLike if TYPE_CHECKING: # pragma: no cover - from .web_urldispatcher import UrlDispatcher + from .web_urldispatcher import ( + UrlDispatcher, + AbstractRoute + ) from .web_request import Request from .web_response import StreamResponse else: - Request = StreamResponse = UrlDispatcher = None + Request = StreamResponse = UrlDispatcher = AbstractRoute = None __all__ = ('AbstractRouteDef', 'RouteDef', 'StaticDef', 'RouteTableDef', @@ -36,7 +39,7 @@ class AbstractRouteDef(abc.ABC): @abc.abstractmethod - def register(self, router: UrlDispatcher) -> None: + def register(self, router: UrlDispatcher) -> List[AbstractRoute]: pass # pragma: no cover @@ -59,13 +62,13 @@ def __repr__(self) -> str: "{info}>".format(method=self.method, path=self.path, handler=self.handler, info=''.join(info))) - def register(self, router: UrlDispatcher) -> None: + def register(self, router: UrlDispatcher) -> List[AbstractRoute]: if self.method in hdrs.METH_ALL: reg = getattr(router, 'add_'+self.method.lower()) - reg(self.path, self.handler, **self.kwargs) + return [reg(self.path, self.handler, **self.kwargs)] else: - router.add_route(self.method, self.path, self.handler, - **self.kwargs) + return [router.add_route(self.method, self.path, self.handler, + **self.kwargs)] @attr.s(frozen=True, repr=False, slots=True) @@ -82,8 +85,10 @@ def __repr__(self) -> str: "{info}>".format(prefix=self.prefix, path=self.path, info=''.join(info))) - def register(self, router: UrlDispatcher) -> None: - router.add_static(self.prefix, self.path, **self.kwargs) + def register(self, router: UrlDispatcher) -> List[AbstractRoute]: + resource = router.add_static(self.prefix, self.path, **self.kwargs) + routes = resource.get_info().get('routes', {}) + return routes.values() def route(method: str, path: str, handler: _HandlerType, diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index 25a4c48a47b..70ee92751ae 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -569,7 +569,8 @@ def _get_file_hash(byte_array: bytes) -> str: def get_info(self) -> Dict[str, Any]: return {'directory': self._directory, - 'prefix': self._prefix} + 'prefix': self._prefix, + 'routes': self._routes} def set_options_route(self, handler: _WebHandler) -> None: if 'OPTIONS' in self._routes: @@ -1126,10 +1127,15 @@ def freeze(self) -> None: for resource in self._resources: resource.freeze() - def add_routes(self, routes: Iterable[AbstractRouteDef]) -> None: + def add_routes(self, + routes: Iterable[AbstractRouteDef]) -> List[AbstractRoute]: """Append routes to route table. Parameter should be a sequence of RouteDef objects. + + Returns a list of registered AbstractRoute instances. """ + registered_routes = [] for route_def in routes: - route_def.register(self) + registered_routes.extend(route_def.register(self)) + return registered_routes diff --git a/docs/web_reference.rst b/docs/web_reference.rst index 55118bffb2b..47e4d4efbde 100644 --- a/docs/web_reference.rst +++ b/docs/web_reference.rst @@ -1460,12 +1460,19 @@ duplicated like one using :meth:`Application.copy`. The table is a :class:`list` of :class:`RouteDef` items or :class:`RouteTableDef`. + :returns: :class:`list` of registered :class:`AbstractRoute` instances. + The method is a shortcut for ``app.router.add_routes(routes_table)``, see also :meth:`UrlDispatcher.add_routes`. .. versionadded:: 3.1 + .. versionchanged:: 3.7 + + Return value updated from ``None`` to :class:`list` of + :class:`AbstractRoute` instances. + .. method:: make_handler(loop=None, **kwargs) Creates HTTP protocol factory for handling requests. @@ -1669,8 +1676,15 @@ Router is any object that implements :class:`AbstractRouter` interface. The table is a :class:`list` of :class:`RouteDef` items or :class:`RouteTableDef`. + :returns: :class:`list` of registered :class:`AbstractRoute` instances. + .. versionadded:: 2.3 + .. versionchanged:: 3.7 + + Return value updated from ``None`` to :class:`list` of + :class:`AbstractRoute` instances. + .. method:: add_get(path, handler, *, name=None, allow_head=True, **kwargs) Shortcut for adding a GET handler. Calls the :meth:`add_route` with \ @@ -2180,6 +2194,13 @@ The definition is created by functions like :func:`get` or Abstract method, should be overridden by subclasses. + :returns: :class:`list` of registered :class:`AbstractRoute` objects. + + .. versionchanged:: 3.7 + + Return value updated from ``None`` to :class:`list` of + :class:`AbstractRoute` instances. + .. class:: RouteDef diff --git a/tests/test_urldispatch.py b/tests/test_urldispatch.py index a81426a747b..a3362138a17 100644 --- a/tests/test_urldispatch.py +++ b/tests/test_urldispatch.py @@ -903,8 +903,11 @@ async def test_match_info_get_info_dynamic2(router) -> None: def test_static_resource_get_info(router) -> None: directory = pathlib.Path(aiohttp.__file__).parent.resolve() resource = router.add_static('/st', directory) - assert resource.get_info() == {'directory': directory, - 'prefix': '/st'} + info = resource.get_info() + assert len(info) == 3 + assert info['directory'] == directory + assert info['prefix'] == '/st' + assert all([type(r) is ResourceRoute for r in info['routes'].values()]) async def test_system_route_get_info(router) -> None: