Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add app.pre_frozen state #3237

Merged
merged 2 commits into from
Sep 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES/3237.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ``app.pre_frozen`` state to properly handle startup signals in sub-applications.
30 changes: 22 additions & 8 deletions aiohttp/web_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class Application(MutableMapping):
ATTRS = frozenset([
'logger', '_debug', '_router', '_loop', '_handler_args',
'_middlewares', '_middlewares_handlers', '_run_middlewares',
'_state', '_frozen', '_subapps',
'_state', '_frozen', '_pre_frozen', '_subapps',
'_on_response_prepare', '_on_startup', '_on_shutdown',
'_on_cleanup', '_client_max_size', '_cleanup_ctx'])

Expand Down Expand Up @@ -68,6 +68,7 @@ def __init__(self, *,
self._run_middlewares = None # initialized on freezing
self._state = {}
self._frozen = False
self._pre_frozen = False
self._subapps = []

self._on_response_prepare = Signal(self) # type: _RespPrepareSignal
Expand Down Expand Up @@ -146,14 +147,14 @@ def _set_loop(self, loop):
subapp._set_loop(loop)

@property
def frozen(self) -> bool:
return self._frozen
def pre_frozen(self) -> bool:
return self._pre_frozen

def freeze(self) -> None:
if self._frozen:
def pre_freeze(self) -> None:
if self._pre_frozen:
return

self._frozen = True
self._pre_frozen = True
self._middlewares.freeze()
self._router.freeze()
self._on_response_prepare.freeze()
Expand All @@ -171,10 +172,23 @@ def freeze(self) -> None:
self._run_middlewares = True if self.middlewares else False

for subapp in self._subapps:
subapp.freeze()
subapp.pre_freeze()
self._run_middlewares =\
self._run_middlewares or subapp._run_middlewares

@property
def frozen(self) -> bool:
return self._frozen

def freeze(self) -> None:
if self._frozen:
return

self.pre_freeze()
self._frozen = True
for subapp in self._subapps:
subapp.freeze()

@property
def debug(self) -> bool:
return self._debug
Expand Down Expand Up @@ -208,7 +222,7 @@ def add_subapp(self, prefix: str, subapp: 'Application'):
self.router.register_resource(resource)
self._reg_subapp_signals(subapp)
self._subapps.append(subapp)
subapp.freeze()
subapp.pre_freeze()
if self._loop is not None:
subapp._set_loop(self._loop)
return resource
Expand Down
80 changes: 78 additions & 2 deletions tests/test_web_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,12 +248,13 @@ async def middleware(request, handler):
assert root._run_middlewares is True


def test_subapp_frozen_after_adding():
def test_subapp_pre_frozen_after_adding():
app = web.Application()
subapp = web.Application()

app.add_subapp('/prefix', subapp)
assert subapp.frozen
assert subapp.pre_frozen
assert not subapp.frozen


@pytest.mark.skipif(not PY_36,
Expand Down Expand Up @@ -455,3 +456,78 @@ async def sub_handler(request):
assert resp.status == 200
resp = await client.get('/sub/')
assert resp.status == 201


async def test_subapp_on_startup(aiohttp_client):

subapp = web.Application()

startup_called = False

async def on_startup(app):
nonlocal startup_called
startup_called = True
app['startup'] = True

subapp.on_startup.append(on_startup)

ctx_pre_called = False
ctx_post_called = False

@async_generator
async def cleanup_ctx(app):
nonlocal ctx_pre_called, ctx_post_called
ctx_pre_called = True
app['cleanup'] = True
await yield_(None)
ctx_post_called = True

subapp.cleanup_ctx.append(cleanup_ctx)

shutdown_called = False

async def on_shutdown(app):
nonlocal shutdown_called
shutdown_called = True

subapp.on_shutdown.append(on_shutdown)

cleanup_called = False

async def on_cleanup(app):
nonlocal cleanup_called
cleanup_called = True

subapp.on_cleanup.append(on_cleanup)

app = web.Application()

app.add_subapp('/subapp', subapp)

assert not startup_called
assert not ctx_pre_called
assert not ctx_post_called
assert not shutdown_called
assert not cleanup_called

assert subapp.on_startup.frozen
assert subapp.cleanup_ctx.frozen
assert subapp.on_shutdown.frozen
assert subapp.on_cleanup.frozen
assert subapp.router.frozen

client = await aiohttp_client(app)

assert startup_called
assert ctx_pre_called
assert not ctx_post_called
assert not shutdown_called
assert not cleanup_called

await client.close()

assert startup_called
assert ctx_pre_called
assert ctx_post_called
assert shutdown_called
assert cleanup_called