diff --git a/aiohttp/web.py b/aiohttp/web.py index d2457a626f1..0f95f7aee93 100644 --- a/aiohttp/web.py +++ b/aiohttp/web.py @@ -18,6 +18,7 @@ from .web_reqrep import * # noqa from .web_server import Server from .web_urldispatcher import * # noqa +from .web_urldispatcher import PrefixedSubAppResource, _wrap_add_subbapp from .web_ws import * # noqa __all__ = (web_reqrep.__all__ + @@ -39,7 +40,7 @@ def __init__(self, *, logger=web_logger, loop=None, router = web_urldispatcher.UrlDispatcher() assert isinstance(router, AbstractRouter), router - router.post_init(self) + router.add_subapp = _wrap_add_subbapp(self) if debug is ...: debug = loop.get_debug() @@ -125,6 +126,23 @@ def handler(app): reg_handler('on_shutdown') reg_handler('on_cleanup') + def add_subapp(self, prefix, subapp): + if self.frozen: + raise RuntimeError( + "Cannot add sub application to frozen application") + if subapp.frozen: + raise RuntimeError("Cannot add frozen application") + if prefix.endswith('/'): + prefix = prefix[:-1] + if prefix in ('', '/'): + raise ValueError("Prefix cannot be empty") + + resource = PrefixedSubAppResource(prefix, subapp) + self.reg_resource(resource) + subapp._reg_subapp_signals(subapp) + subapp.freeze() + return resource + @property def on_response_prepare(self): return self._on_response_prepare diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index 017452d0b6f..b0e443475c5 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -714,11 +714,6 @@ def __init__(self): super().__init__() self._resources = [] self._named_resources = {} - self._app = None - - def post_init(self, app): - assert app is not None - self._app = app @asyncio.coroutine def resolve(self, request): @@ -759,16 +754,13 @@ def routes(self): def named_resources(self): return MappingProxyType(self._named_resources) - def _reg_resource(self, resource): + def reg_resource(self, resource): assert isinstance(resource, AbstractResource), \ 'Instance of AbstractResource class is required, got {!r}'.format( resource) - if self._app is None: - raise RuntimeError(".post_init() should be called before " - "first resource registering") if self.frozen: - raise RuntimeError("Cannot register a resource into " - "frozen router.") + raise RuntimeError( + "Cannot register a resource into frozen router.") name = resource.name @@ -792,7 +784,7 @@ def add_resource(self, path, *, name=None): raise ValueError("path should be started with / or be empty") if not ('{' in path or '}' in path or self.ROUTE_RE.search(path)): resource = PlainResource(quote(path, safe='/'), name=name) - self._reg_resource(resource) + self.reg_resource(resource) return resource pattern = '' @@ -823,7 +815,7 @@ def add_resource(self, path, *, name=None): raise ValueError( "Bad pattern '{}': {}".format(pattern, exc)) from None resource = DynamicResource(compiled, formatter, name=name) - self._reg_resource(resource) + self.reg_resource(resource) return resource def add_route(self, method, path, handler, @@ -852,7 +844,7 @@ def add_static(self, prefix, path, *, name=None, expect_handler=None, response_factory=response_factory, show_index=show_index, follow_symlinks=follow_symlinks) - self._reg_resource(resource) + self.reg_resource(resource) return resource def add_head(self, *args, **kwargs): @@ -891,20 +883,25 @@ def add_delete(self, *args, **kwargs): """ return self.add_route(hdrs.METH_DELETE, *args, **kwargs) - def add_subapp(self, prefix, subapp): + def freeze(self): + super().freeze() + for resource in self._resources: + resource.freeze() + + +def _wrap_add_subbapp(app): + + def add_subapp(prefix, subapp): if subapp.frozen: - raise RuntimeError("Cannod add frozen application") + raise RuntimeError("Cannot add frozen application") if prefix.endswith('/'): prefix = prefix[:-1] if prefix in ('', '/'): raise ValueError("Prefix cannot be empty") resource = PrefixedSubAppResource(prefix, subapp) - self._reg_resource(resource) - self._app._reg_subapp_signals(subapp) + app.router.reg_resource(resource) + app._reg_subapp_signals(subapp) subapp.freeze() return resource - def freeze(self): - super().freeze() - for resource in self._resources: - resource.freeze() + return add_subapp diff --git a/tests/test_urldispatch.py b/tests/test_urldispatch.py index 996a8c29582..a3819d09232 100644 --- a/tests/test_urldispatch.py +++ b/tests/test_urldispatch.py @@ -13,7 +13,7 @@ from aiohttp.test_utils import make_mocked_request from aiohttp.web import HTTPMethodNotAllowed, HTTPNotFound, Response from aiohttp.web_urldispatcher import (AbstractResource, ResourceRoute, - SystemRoute, UrlDispatcher, View, + SystemRoute, View, _defaultExpectHandler) @@ -1011,9 +1011,3 @@ def test_convert_empty_path_to_slash_on_freezing(router): assert resource.get_info() == {'path': ''} router.freeze() assert resource.get_info() == {'path': '/'} - - -def test_add_to_non_initialized_router(): - router = UrlDispatcher() - with pytest.raises(RuntimeError): - router.add_get('/', make_handler())