Skip to content

Commit

Permalink
Add workarounds to fly on aiohttp >= 1.1
Browse files Browse the repository at this point in the history
Since aiohttp 1.1 some backward incompatible changes have been
implemented. Despite each of them has its own issue on GitHub:

  - aio-libs/aiohttp#1373
  - aio-libs/aiohttp#1416

it's not clear when they become resolved. In order to be able to
fly on latest aiohttp version, this commit resolves those issues
by implementing workarounds on our side.
  • Loading branch information
Ihor Kalnytskyi committed Dec 7, 2016
1 parent 659e4a8 commit c49299b
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 20 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
'pytest-runner',
],
install_requires=[
'aiohttp >= 0.21.2, < 1.1',
'aiohttp >= 1.1',
'cerberus >= 0.9.2',
'motor >= 0.7, < 1.0',
'python-jose >= 1.3.2',
Expand Down
2 changes: 1 addition & 1 deletion tests/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def setup(self):
@pytest.mark.parametrize('name, value', [
('Accept', 'application/json'),
('Accept-Encoding', 'gzip'),
('Api-Version', '1'),
('Api-Version', '1.0'),
])
async def test_http_vary_header(self, name, value):
async with AIOTestApp(self.app) as testapp:
Expand Down
17 changes: 13 additions & 4 deletions tests/test_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,27 @@ async def get(self):
return web.Response(text='I am Batman!')

def setup(self):
router_v1 = web_urldispatcher.UrlDispatcher()
self.app = web.Application()

router_v1 = web_urldispatcher.UrlDispatcher(self.app)
router_v1.add_route('*', '/test', self._TestResource1)

router_v2 = web_urldispatcher.UrlDispatcher()
router_v2 = web_urldispatcher.UrlDispatcher(self.app)
router_v2.add_route('*', '/test', self._TestResource2)

self.app = web.Application(router=router.VersionRouter(
# Since aiohttp 1.1, UrlDispatcher has one mandatory attribute -
# application instance, that's used internally only in subapps
# feature. Unfortunately, there's no legal way to pass application
# when router is created or vice versa. Either way we need to
# access internal variable in order to do so.
#
# See https://github.com/KeepSafe/aiohttp/issues/1373 for details.
self.app._router = router.VersionRouter(
{
'1': router_v1,
'2': router_v2,
}
))
)

async def test_version_1(self):
async with AIOTestApp(self.app) as testapp:
Expand Down
32 changes: 20 additions & 12 deletions xsnippet_api/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,30 @@ def create_app(conf):
:return: an application instance
:rtype: :class:`aiohttp.web.Application`
"""
# we need to import all the resources in order to evaluate @endpoint
# decorator, so they can be collected and passed to VersionRouter
# We need to import all the resources in order to evaluate @endpoint
# decorator, so they can be collected and passed to VersionRouter.
from . import resources # noqa
app = web.Application(
router=router.VersionRouter(endpoint.collect()),
middlewares=[
functools.partial(middlewares.auth, conf['auth']),
])

# attach settings to the application instance in order to make them
# accessible at any point of execution (e.g. request handling)
# Since aiohttp 1.1, UrlDispatcher has one mandatory attribute -
# application instance, that's used internally only in subapps
# feature. Unfortunately, there's no legal way to pass application
# when router is created or vice versa. Either way we need to
# access internal variable in order to do so.
#
# See https://github.com/KeepSafe/aiohttp/issues/1373 for details.
app._router = router.VersionRouter(endpoint.collect(app))

# Attach settings to the application instance in order to make them
# accessible at any point of execution (e.g. request handling).
app['conf'] = conf
app['db'] = database.create_connection(conf)

# we need to respond with Vary header time to time in order to avoid
# issues with cache on client side
# We need to respond with Vary header time to time in order to avoid
# issues with cache on client side.
app.on_response_prepare.append(_inject_vary_header)

return app
Expand Down Expand Up @@ -109,18 +117,18 @@ def __call__(self, resource):
return resource

@classmethod
def collect(cls):
rv = collections.defaultdict(web_urldispatcher.UrlDispatcher)
def collect(cls, app):
rv = {}

# Create routers for each discovered API version. The main reason why
# we need that so early is to register resources in all needed routers
# according to supported version range.
for item in cls._registry:
rv[item.version]
rv[item.version] = web_urldispatcher.UrlDispatcher(app)

for item in cls._registry:
# if there's no end_version then a resource is still working, and
# latest discovered version should be considered as end_version
# If there's no end_version then a resource is still working, and
# latest discovered version should be considered as end_version.
end_version = item.end_version or sorted(
rv.keys(),
key=pkg_resources.parse_version
Expand Down
6 changes: 4 additions & 2 deletions xsnippet_api/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import pkg_resources

from aiohttp import abc, web
from aiohttp import abc, web, web_urldispatcher


def _get_latest_version(versions, stable=True):
Expand Down Expand Up @@ -77,6 +77,8 @@ async def resolve(self, request):
version = self._latest

if version not in self._routers:
raise web.HTTPPreconditionFailed()
return web_urldispatcher.MatchInfoError(
web.HTTPPreconditionFailed()
)

return await self._routers[version].resolve(request)

0 comments on commit c49299b

Please sign in to comment.