Skip to content

Commit 864ef49

Browse files
authored
Add app.pre_frozen state (#3237)
1 parent 7a0359f commit 864ef49

File tree

3 files changed

+101
-10
lines changed

3 files changed

+101
-10
lines changed

CHANGES/3237.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add ``app.pre_frozen`` state to properly handle startup signals in sub-applications.

aiohttp/web_app.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class Application(MutableMapping):
3434
ATTRS = frozenset([
3535
'logger', '_debug', '_router', '_loop', '_handler_args',
3636
'_middlewares', '_middlewares_handlers', '_run_middlewares',
37-
'_state', '_frozen', '_subapps',
37+
'_state', '_frozen', '_pre_frozen', '_subapps',
3838
'_on_response_prepare', '_on_startup', '_on_shutdown',
3939
'_on_cleanup', '_client_max_size', '_cleanup_ctx'])
4040

@@ -68,6 +68,7 @@ def __init__(self, *,
6868
self._run_middlewares = None # initialized on freezing
6969
self._state = {}
7070
self._frozen = False
71+
self._pre_frozen = False
7172
self._subapps = []
7273

7374
self._on_response_prepare = Signal(self) # type: _RespPrepareSignal
@@ -146,14 +147,14 @@ def _set_loop(self, loop):
146147
subapp._set_loop(loop)
147148

148149
@property
149-
def frozen(self) -> bool:
150-
return self._frozen
150+
def pre_frozen(self) -> bool:
151+
return self._pre_frozen
151152

152-
def freeze(self) -> None:
153-
if self._frozen:
153+
def pre_freeze(self) -> None:
154+
if self._pre_frozen:
154155
return
155156

156-
self._frozen = True
157+
self._pre_frozen = True
157158
self._middlewares.freeze()
158159
self._router.freeze()
159160
self._on_response_prepare.freeze()
@@ -171,10 +172,23 @@ def freeze(self) -> None:
171172
self._run_middlewares = True if self.middlewares else False
172173

173174
for subapp in self._subapps:
174-
subapp.freeze()
175+
subapp.pre_freeze()
175176
self._run_middlewares =\
176177
self._run_middlewares or subapp._run_middlewares
177178

179+
@property
180+
def frozen(self) -> bool:
181+
return self._frozen
182+
183+
def freeze(self) -> None:
184+
if self._frozen:
185+
return
186+
187+
self.pre_freeze()
188+
self._frozen = True
189+
for subapp in self._subapps:
190+
subapp.freeze()
191+
178192
@property
179193
def debug(self) -> bool:
180194
return self._debug
@@ -208,7 +222,7 @@ def add_subapp(self, prefix: str, subapp: 'Application'):
208222
self.router.register_resource(resource)
209223
self._reg_subapp_signals(subapp)
210224
self._subapps.append(subapp)
211-
subapp.freeze()
225+
subapp.pre_freeze()
212226
if self._loop is not None:
213227
subapp._set_loop(self._loop)
214228
return resource

tests/test_web_app.py

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,12 +248,13 @@ async def middleware(request, handler):
248248
assert root._run_middlewares is True
249249

250250

251-
def test_subapp_frozen_after_adding():
251+
def test_subapp_pre_frozen_after_adding():
252252
app = web.Application()
253253
subapp = web.Application()
254254

255255
app.add_subapp('/prefix', subapp)
256-
assert subapp.frozen
256+
assert subapp.pre_frozen
257+
assert not subapp.frozen
257258

258259

259260
@pytest.mark.skipif(not PY_36,
@@ -455,3 +456,78 @@ async def sub_handler(request):
455456
assert resp.status == 200
456457
resp = await client.get('/sub/')
457458
assert resp.status == 201
459+
460+
461+
async def test_subapp_on_startup(aiohttp_client):
462+
463+
subapp = web.Application()
464+
465+
startup_called = False
466+
467+
async def on_startup(app):
468+
nonlocal startup_called
469+
startup_called = True
470+
app['startup'] = True
471+
472+
subapp.on_startup.append(on_startup)
473+
474+
ctx_pre_called = False
475+
ctx_post_called = False
476+
477+
@async_generator
478+
async def cleanup_ctx(app):
479+
nonlocal ctx_pre_called, ctx_post_called
480+
ctx_pre_called = True
481+
app['cleanup'] = True
482+
await yield_(None)
483+
ctx_post_called = True
484+
485+
subapp.cleanup_ctx.append(cleanup_ctx)
486+
487+
shutdown_called = False
488+
489+
async def on_shutdown(app):
490+
nonlocal shutdown_called
491+
shutdown_called = True
492+
493+
subapp.on_shutdown.append(on_shutdown)
494+
495+
cleanup_called = False
496+
497+
async def on_cleanup(app):
498+
nonlocal cleanup_called
499+
cleanup_called = True
500+
501+
subapp.on_cleanup.append(on_cleanup)
502+
503+
app = web.Application()
504+
505+
app.add_subapp('/subapp', subapp)
506+
507+
assert not startup_called
508+
assert not ctx_pre_called
509+
assert not ctx_post_called
510+
assert not shutdown_called
511+
assert not cleanup_called
512+
513+
assert subapp.on_startup.frozen
514+
assert subapp.cleanup_ctx.frozen
515+
assert subapp.on_shutdown.frozen
516+
assert subapp.on_cleanup.frozen
517+
assert subapp.router.frozen
518+
519+
client = await aiohttp_client(app)
520+
521+
assert startup_called
522+
assert ctx_pre_called
523+
assert not ctx_post_called
524+
assert not shutdown_called
525+
assert not cleanup_called
526+
527+
await client.close()
528+
529+
assert startup_called
530+
assert ctx_pre_called
531+
assert ctx_post_called
532+
assert shutdown_called
533+
assert cleanup_called

0 commit comments

Comments
 (0)