Skip to content

Commit 0cd24ab

Browse files
author
Nikolay Kim
authored
Merge pull request #1510 from KeepSafe/refactor-subapps
remove Application dependency from UrlDispatcher
2 parents 6da8ae9 + 12ce02b commit 0cd24ab

7 files changed

+93
-77
lines changed

CHANGES.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ CHANGES
66

77
- Fix polls demo run application #1487
88

9-
-
9+
- remove `web.Application` dependency from `web.UrlDispatcher` #1510
1010

11+
1112
1.2.0 (2016-12-17)
1213
------------------
1314

aiohttp/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = '1.2.0'
1+
__version__ = '1.2.1a'
22

33
# Deprecated, keep it here for a while for backward compatibility.
44
import multidict # noqa

aiohttp/web.py

+30-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from .web_reqrep import * # noqa
1919
from .web_server import Server
2020
from .web_urldispatcher import * # noqa
21+
from .web_urldispatcher import PrefixedSubAppResource
2122
from .web_ws import * # noqa
2223

2324
__all__ = (web_reqrep.__all__ +
@@ -39,7 +40,8 @@ def __init__(self, *, logger=web_logger, loop=None,
3940
router = web_urldispatcher.UrlDispatcher()
4041
assert isinstance(router, AbstractRouter), router
4142

42-
router.post_init(self)
43+
# backward compatibility until full deprecation
44+
router.add_subapp = _wrap_add_subbapp(self)
4345

4446
if debug is ...:
4547
debug = loop.get_debug()
@@ -125,6 +127,23 @@ def handler(app):
125127
reg_handler('on_shutdown')
126128
reg_handler('on_cleanup')
127129

130+
def add_subapp(self, prefix, subapp):
131+
if self.frozen:
132+
raise RuntimeError(
133+
"Cannot add sub application to frozen application")
134+
if subapp.frozen:
135+
raise RuntimeError("Cannot add frozen application")
136+
if prefix.endswith('/'):
137+
prefix = prefix[:-1]
138+
if prefix in ('', '/'):
139+
raise ValueError("Prefix cannot be empty")
140+
141+
resource = PrefixedSubAppResource(prefix, subapp)
142+
self.router.reg_resource(resource)
143+
self._reg_subapp_signals(subapp)
144+
subapp.freeze()
145+
return resource
146+
128147
@property
129148
def on_response_prepare(self):
130149
return self._on_response_prepare
@@ -264,6 +283,16 @@ def __repr__(self):
264283
return "<Application 0x{:x}>".format(id(self))
265284

266285

286+
def _wrap_add_subbapp(app):
287+
# backward compatibility
288+
289+
def add_subapp(prefix, subapp):
290+
warnings.warn("Use app.add_subapp() instead", DeprecationWarning)
291+
return app.add_subapp(prefix, subapp)
292+
293+
return add_subapp
294+
295+
267296
def run_app(app, *, host='0.0.0.0', port=None,
268297
shutdown_timeout=60.0, ssl_context=None,
269298
print=print, backlog=128, access_log_format=None,

aiohttp/web_urldispatcher.py

+6-27
Original file line numberDiff line numberDiff line change
@@ -714,11 +714,6 @@ def __init__(self):
714714
super().__init__()
715715
self._resources = []
716716
self._named_resources = {}
717-
self._app = None
718-
719-
def post_init(self, app):
720-
assert app is not None
721-
self._app = app
722717

723718
@asyncio.coroutine
724719
def resolve(self, request):
@@ -759,16 +754,13 @@ def routes(self):
759754
def named_resources(self):
760755
return MappingProxyType(self._named_resources)
761756

762-
def _reg_resource(self, resource):
757+
def reg_resource(self, resource):
763758
assert isinstance(resource, AbstractResource), \
764759
'Instance of AbstractResource class is required, got {!r}'.format(
765760
resource)
766-
if self._app is None:
767-
raise RuntimeError(".post_init() should be called before "
768-
"first resource registering")
769761
if self.frozen:
770-
raise RuntimeError("Cannot register a resource into "
771-
"frozen router.")
762+
raise RuntimeError(
763+
"Cannot register a resource into frozen router.")
772764

773765
name = resource.name
774766

@@ -792,7 +784,7 @@ def add_resource(self, path, *, name=None):
792784
raise ValueError("path should be started with / or be empty")
793785
if not ('{' in path or '}' in path or self.ROUTE_RE.search(path)):
794786
resource = PlainResource(quote(path, safe='/'), name=name)
795-
self._reg_resource(resource)
787+
self.reg_resource(resource)
796788
return resource
797789

798790
pattern = ''
@@ -823,7 +815,7 @@ def add_resource(self, path, *, name=None):
823815
raise ValueError(
824816
"Bad pattern '{}': {}".format(pattern, exc)) from None
825817
resource = DynamicResource(compiled, formatter, name=name)
826-
self._reg_resource(resource)
818+
self.reg_resource(resource)
827819
return resource
828820

829821
def add_route(self, method, path, handler,
@@ -852,7 +844,7 @@ def add_static(self, prefix, path, *, name=None, expect_handler=None,
852844
response_factory=response_factory,
853845
show_index=show_index,
854846
follow_symlinks=follow_symlinks)
855-
self._reg_resource(resource)
847+
self.reg_resource(resource)
856848
return resource
857849

858850
def add_head(self, *args, **kwargs):
@@ -891,19 +883,6 @@ def add_delete(self, *args, **kwargs):
891883
"""
892884
return self.add_route(hdrs.METH_DELETE, *args, **kwargs)
893885

894-
def add_subapp(self, prefix, subapp):
895-
if subapp.frozen:
896-
raise RuntimeError("Cannod add frozen application")
897-
if prefix.endswith('/'):
898-
prefix = prefix[:-1]
899-
if prefix in ('', '/'):
900-
raise ValueError("Prefix cannot be empty")
901-
resource = PrefixedSubAppResource(prefix, subapp)
902-
self._reg_resource(resource)
903-
self._app._reg_subapp_signals(subapp)
904-
subapp.freeze()
905-
return resource
906-
907886
def freeze(self):
908887
super().freeze()
909888
for resource in self._resources:

docs/web.rst

+4-4
Original file line numberDiff line numberDiff line change
@@ -971,12 +971,12 @@ toolbar URLs are served by prefix like ``/admin``.
971971

972972
Thus we'll create a totally separate application named ``admin`` and
973973
connect it to main app with prefix by
974-
:meth:`~aiohttp.web.UrlDispatcher.add_subapp`::
974+
:meth:`~aiohttp.web.Application.add_subapp`::
975975

976976
admin = web.Application()
977977
# setup admin routes, signals and middlewares
978978

979-
app.router.add_subapp('/admin/', admin)
979+
app.add_subapp('/admin/', admin)
980980

981981
Middlewares and signals from ``app`` and ``admin`` are chained.
982982

@@ -1006,7 +1006,7 @@ But for getting URL sub-application's router should be used::
10061006
admin = web.Application()
10071007
admin.router.add_get('/resource', handler, name='name')
10081008

1009-
app.router.add_subapp('/admin/', admin)
1009+
app.add_subapp('/admin/', admin)
10101010

10111011
url = admin.router['name'].url_for()
10121012

@@ -1019,7 +1019,7 @@ use the following explicit technique::
10191019
admin = web.Application()
10201020
admin.router.add_get('/resource', handler, name='name')
10211021

1022-
app.router.add_subapp('/admin/', admin)
1022+
app.add_subapp('/admin/', admin)
10231023
app['admin'] = admin
10241024

10251025
async def handler(request): # main application's handler

tests/test_urldispatch.py

+31-24
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from aiohttp.test_utils import make_mocked_request
1414
from aiohttp.web import HTTPMethodNotAllowed, HTTPNotFound, Response
1515
from aiohttp.web_urldispatcher import (AbstractResource, ResourceRoute,
16-
SystemRoute, UrlDispatcher, View,
16+
SystemRoute, View,
1717
_defaultExpectHandler)
1818

1919

@@ -907,46 +907,52 @@ def test_url_for_in_resource_route(router):
907907
assert URL('/get/John') == route.url_for(name='John')
908908

909909

910-
def test_subapp_get_info(router, loop):
910+
def test_subapp_get_info(app, loop):
911911
subapp = web.Application(loop=loop)
912-
resource = router.add_subapp('/pre', subapp)
912+
resource = subapp.add_subapp('/pre', subapp)
913913
assert resource.get_info() == {'prefix': '/pre', 'app': subapp}
914914

915915

916-
def test_subapp_url(router, loop):
916+
def test_subapp_backward_compatible(router, loop):
917917
subapp = web.Application(loop=loop)
918918
resource = router.add_subapp('/pre', subapp)
919+
assert resource.get_info() == {'prefix': '/pre', 'app': subapp}
920+
921+
922+
def test_subapp_url(app, loop):
923+
subapp = web.Application(loop=loop)
924+
resource = app.add_subapp('/pre', subapp)
919925
with pytest.raises(RuntimeError):
920926
resource.url()
921927

922928

923-
def test_subapp_url_for(router, loop):
929+
def test_subapp_url_for(app, loop):
924930
subapp = web.Application(loop=loop)
925-
resource = router.add_subapp('/pre', subapp)
931+
resource = app.add_subapp('/pre', subapp)
926932
with pytest.raises(RuntimeError):
927933
resource.url_for()
928934

929935

930-
def test_subapp_repr(router, loop):
936+
def test_subapp_repr(app, loop):
931937
subapp = web.Application(loop=loop)
932-
resource = router.add_subapp('/pre', subapp)
938+
resource = app.add_subapp('/pre', subapp)
933939
assert repr(resource).startswith(
934940
'<PrefixedSubAppResource /pre -> <Application')
935941

936942

937-
def test_subapp_len(router, loop):
943+
def test_subapp_len(app, loop):
938944
subapp = web.Application(loop=loop)
939945
subapp.router.add_get('/', make_handler())
940946
subapp.router.add_post('/', make_handler())
941-
resource = router.add_subapp('/pre', subapp)
947+
resource = app.add_subapp('/pre', subapp)
942948
assert len(resource) == 2
943949

944950

945-
def test_subapp_iter(router, loop):
951+
def test_subapp_iter(app, loop):
946952
subapp = web.Application(loop=loop)
947953
r1 = subapp.router.add_get('/', make_handler())
948954
r2 = subapp.router.add_post('/', make_handler())
949-
resource = router.add_subapp('/pre', subapp)
955+
resource = app.add_subapp('/pre', subapp)
950956
assert list(resource) == [r1, r2]
951957

952958

@@ -961,11 +967,18 @@ def test_frozen_router(router):
961967
router.add_get('/', make_handler())
962968

963969

964-
def test_frozen_router_subapp(loop, router):
970+
def test_frozen_router_subapp(app, loop):
965971
subapp = web.Application(loop=loop)
966972
subapp.freeze()
967973
with pytest.raises(RuntimeError):
968-
router.add_subapp('/', subapp)
974+
app.add_subapp('/', subapp)
975+
976+
977+
def test_frozen_app_on_subapp(app, loop):
978+
app.freeze()
979+
subapp = web.Application(loop=loop)
980+
with pytest.raises(RuntimeError):
981+
app.add_subapp('/', subapp)
969982

970983

971984
def test_set_options_route(router):
@@ -991,16 +1004,16 @@ def test_dynamic_url_with_name_started_from_undescore(router):
9911004
assert URL('/get/John') == route.url_for(_name='John')
9921005

9931006

994-
def test_cannot_add_subapp_with_empty_prefix(router, loop):
1007+
def test_cannot_add_subapp_with_empty_prefix(app, loop):
9951008
subapp = web.Application(loop=loop)
9961009
with pytest.raises(ValueError):
997-
router.add_subapp('', subapp)
1010+
app.add_subapp('', subapp)
9981011

9991012

1000-
def test_cannot_add_subapp_with_slash_prefix(router, loop):
1013+
def test_cannot_add_subapp_with_slash_prefix(app, loop):
10011014
subapp = web.Application(loop=loop)
10021015
with pytest.raises(ValueError):
1003-
router.add_subapp('/', subapp)
1016+
app.add_subapp('/', subapp)
10041017

10051018

10061019
@asyncio.coroutine
@@ -1011,9 +1024,3 @@ def test_convert_empty_path_to_slash_on_freezing(router):
10111024
assert resource.get_info() == {'path': ''}
10121025
router.freeze()
10131026
assert resource.get_info() == {'path': '/'}
1014-
1015-
1016-
def test_add_to_non_initialized_router():
1017-
router = UrlDispatcher()
1018-
with pytest.raises(RuntimeError):
1019-
router.add_get('/', make_handler())

0 commit comments

Comments
 (0)