Skip to content

Commit

Permalink
Subapps (#1301)
Browse files Browse the repository at this point in the history
* Change AbstractResource.resolve signature

* Work in progress

* Add a section about subapps

* Work on subapps

* Add a test for reverse URL usage by subapp

* Fix pep8 error

* Fix tests

* Fix docs spelling errors

* Improve test coverage

* Improve coverage

* Better coverage

* Work on coverage

* Add another test

* Another test

* Add another test

* Support nested subapp middlewares

* Support signals in subapplication
  • Loading branch information
asvetlov authored Oct 27, 2016
1 parent a610cf1 commit 4bef388
Show file tree
Hide file tree
Showing 14 changed files with 630 additions and 111 deletions.
23 changes: 23 additions & 0 deletions aiohttp/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,29 @@ def http_exception(self):
def get_info(self):
"""Return a dict with additional info useful for introspection"""

@property # pragma: no branch
@abstractmethod
def apps(self):
"""Stack of nested applications.
Top level application is left-most element.
"""

@abstractmethod
def add_app(self, app):
"""Add application to the nested apps stack."""

@abstractmethod
def freeze(self):
"""Freeze the match info.
The method is called after route resolution.
After the call .add_app() is forbidden.
"""


class AbstractView(ABC):

Expand Down
9 changes: 7 additions & 2 deletions aiohttp/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from .helpers import sentinel
from .protocol import HttpVersion, RawRequestMessage
from .signals import Signal
from .web import Application, Request
from .web import Application, Request, UrlMappingMatchInfo

PY_35 = sys.version_info >= (3, 5)

Expand Down Expand Up @@ -66,6 +66,7 @@ def start_server(self, **kwargs):
self._root = URL('{}://{}:{}'.format(self.scheme,
self.host,
self.port))
yield from self.app.startup()
self.handler = self.app.make_handler(**kwargs)
self.server = yield from self._loop.create_server(self.handler,
self.host,
Expand Down Expand Up @@ -502,10 +503,14 @@ def make_mocked_request(method, path, headers=None, *,
if payload is sentinel:
payload = mock.Mock()

req = Request(app, message, payload,
req = Request(message, payload,
transport, reader, writer,
secure_proxy_ssl_header=secure_proxy_ssl_header)

match_info = UrlMappingMatchInfo({}, mock.Mock())
match_info.add_app(app)
req._match_info = match_info

return req


Expand Down
19 changes: 13 additions & 6 deletions aiohttp/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ def __init__(self, manager, app, router, *,
self._manager = manager
self._app = app
self._router = router
self._middlewares = app.middlewares
self._secure_proxy_ssl_header = secure_proxy_ssl_header

def __repr__(self):
Expand All @@ -65,15 +64,16 @@ def handle_request(self, message, payload):

app = self._app
request = web_reqrep.Request(
app, message, payload,
message, payload,
self.transport, self.reader, self.writer,
secure_proxy_ssl_header=self._secure_proxy_ssl_header)
self._meth = request.method
self._path = request.path
try:
match_info = yield from self._router.resolve(request)

assert isinstance(match_info, AbstractMatchInfo), match_info
match_info.add_app(app)
match_info.freeze()

resp = None
request._match_info = match_info
Expand All @@ -84,7 +84,7 @@ def handle_request(self, message, payload):

if resp is None:
handler = match_info.handler
for factory in reversed(self._middlewares):
for factory in match_info.middlewares:
handler = yield from factory(app, handler)
resp = yield from handler(request)

Expand Down Expand Up @@ -166,7 +166,7 @@ def __init__(self, *, logger=web_logger, loop=None,
if loop is None:
loop = asyncio.get_event_loop()
if router is None:
router = web_urldispatcher.UrlDispatcher()
router = web_urldispatcher.UrlDispatcher(self)
assert isinstance(router, AbstractRouter), router

self._debug = debug
Expand All @@ -188,6 +188,13 @@ def __init__(self, *, logger=web_logger, loop=None,
def debug(self):
return self._debug

def _reg_subapp_signals(self, subapp):
self._on_pre_signal.extend(subapp.on_pre_signal)
self._on_post_signal.extend(subapp.on_post_signal)
self._on_startup.extend(subapp.on_startup)
self._on_shutdown.extend(subapp.on_shutdown)
self._on_cleanup.extend(subapp.on_cleanup)

@property
def on_response_prepare(self):
return self._on_response_prepare
Expand Down Expand Up @@ -288,7 +295,7 @@ def __call__(self):
return self

def __repr__(self):
return "<Application>"
return "<Application 0x{:x}>".format(id(self))


def run_app(app, *, host='0.0.0.0', port=None,
Expand Down
11 changes: 6 additions & 5 deletions aiohttp/web_reqrep.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ class Request(dict, HeadersMixin):
POST_METHODS = {hdrs.METH_PATCH, hdrs.METH_POST, hdrs.METH_PUT,
hdrs.METH_TRACE, hdrs.METH_DELETE}

def __init__(self, app, message, payload, transport, reader, writer, *,
def __init__(self, message, payload, transport, reader, writer, *,
secure_proxy_ssl_header=None):
self._app = app
self._app = None
self._message = message
self._transport = transport
self._reader = reader
Expand Down Expand Up @@ -274,10 +274,10 @@ def match_info(self):
"""Result of route resolving."""
return self._match_info

@property
@reify
def app(self):
"""Application instance."""
return self._app
return self._match_info.apps[-1]

@property
def transport(self):
Expand Down Expand Up @@ -724,7 +724,8 @@ def prepare(self, request):
resp_impl = self._start_pre_check(request)
if resp_impl is not None:
return resp_impl
yield from request.app.on_response_prepare.send(request, self)
for app in request.match_info.apps:
yield from app.on_response_prepare.send(request, self)

return self._start(request)

Expand Down
Loading

0 comments on commit 4bef388

Please sign in to comment.