From 5b2ff779c39563146386f244468dc788bfcb9a2a Mon Sep 17 00:00:00 2001
From: Andrew Svetlov <andrew.svetlov@gmail.com>
Date: Wed, 13 Dec 2017 16:51:46 +0200
Subject: [PATCH] Switch to async/await syntax

---
 .travis.yml                          |   1 -
 aiohttp_security/abc.py              |  16 +--
 aiohttp_security/api.py              |  46 +++----
 aiohttp_security/cookies_identity.py |  13 +-
 aiohttp_security/session_identity.py |  17 +--
 demo/database_auth/db_auth.py        |  29 ++--
 demo/database_auth/handlers.py       |  33 ++---
 demo/database_auth/main.py           |  16 +--
 demo/dictionary_auth/handlers.py     |   8 +-
 docs/example.rst                     |  23 ++--
 docs/example_db_auth.rst             |  52 ++++----
 tests/test_cookies_identity.py       |  68 ++++------
 tests/test_dict_autz.py              | 189 +++++++++++----------------
 tests/test_no_auth.py                |  32 ++---
 tests/test_no_identity.py            |  28 ++--
 tests/test_session_identity.py       |  79 +++++------
 16 files changed, 257 insertions(+), 393 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 128105e..723b760 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,5 @@
 language: python
 python:
-  - 3.4.3
   - 3.5
   - 3.6
   - 3.7-dev
diff --git a/aiohttp_security/abc.py b/aiohttp_security/abc.py
index b1f9f2a..862abd8 100644
--- a/aiohttp_security/abc.py
+++ b/aiohttp_security/abc.py
@@ -1,21 +1,18 @@
 import abc
-import asyncio
 
 # see http://plope.com/pyramid_auth_design_api_postmortem
 
 
 class AbstractIdentityPolicy(metaclass=abc.ABCMeta):
 
-    @asyncio.coroutine
     @abc.abstractmethod
-    def identify(self, request):
+    async def identify(self, request):
         """Return the claimed identity of the user associated request or
         ``None`` if no identity can be found associated with the request."""
         pass
 
-    @asyncio.coroutine
     @abc.abstractmethod
-    def remember(self, request, response, identity, **kwargs):
+    async def remember(self, request, response, identity, **kwargs):
         """Remember identity.
 
         Modify response object by filling it's headers with remembered user.
@@ -25,9 +22,8 @@ class AbstractIdentityPolicy(metaclass=abc.ABCMeta):
         """
         pass
 
-    @asyncio.coroutine
     @abc.abstractmethod
-    def forget(self, request, response):
+    async def forget(self, request, response):
         """ Modify response which can be used to 'forget' the
         current identity on subsequent requests."""
         pass
@@ -35,9 +31,8 @@ class AbstractIdentityPolicy(metaclass=abc.ABCMeta):
 
 class AbstractAuthorizationPolicy(metaclass=abc.ABCMeta):
 
-    @asyncio.coroutine
     @abc.abstractmethod
-    def permits(self, identity, permission, context=None):
+    async def permits(self, identity, permission, context=None):
         """Check user permissions.
 
         Return True if the identity is allowed the permission in the
@@ -45,9 +40,8 @@ class AbstractAuthorizationPolicy(metaclass=abc.ABCMeta):
         """
         pass
 
-    @asyncio.coroutine
     @abc.abstractmethod
-    def authorized_userid(self, identity):
+    async def authorized_userid(self, identity):
         """Retrieve authorized user id.
 
         Return the user_id of the user identified by the identity
diff --git a/aiohttp_security/api.py b/aiohttp_security/api.py
index 41f5d20..0dea731 100644
--- a/aiohttp_security/api.py
+++ b/aiohttp_security/api.py
@@ -1,4 +1,3 @@
-import asyncio
 import enum
 from aiohttp import web
 from aiohttp_security.abc import (AbstractIdentityPolicy,
@@ -9,8 +8,7 @@ IDENTITY_KEY = 'aiohttp_security_identity_policy'
 AUTZ_KEY = 'aiohttp_security_autz_policy'
 
 
-@asyncio.coroutine
-def remember(request, response, identity, **kwargs):
+async def remember(request, response, identity, **kwargs):
     """Remember identity into response.
 
     The action is performed by identity_policy.remember()
@@ -28,11 +26,10 @@ def remember(request, response, identity, **kwargs):
         # output and rendered page we add same message to *reason* and
         # *text* arguments.
         raise web.HTTPInternalServerError(reason=text, text=text)
-    yield from identity_policy.remember(request, response, identity, **kwargs)
+    await identity_policy.remember(request, response, identity, **kwargs)
 
 
-@asyncio.coroutine
-def forget(request, response):
+async def forget(request, response):
     """Forget previously remembered identity.
 
     Usually it clears cookie or server-side storage to forget user
@@ -46,38 +43,35 @@ def forget(request, response):
         # output and rendered page we add same message to *reason* and
         # *text* arguments.
         raise web.HTTPInternalServerError(reason=text, text=text)
-    yield from identity_policy.forget(request, response)
+    await identity_policy.forget(request, response)
 
 
-@asyncio.coroutine
-def authorized_userid(request):
+async def authorized_userid(request):
     identity_policy = request.app.get(IDENTITY_KEY)
     autz_policy = request.app.get(AUTZ_KEY)
     if identity_policy is None or autz_policy is None:
         return None
-    identity = yield from identity_policy.identify(request)
+    identity = await identity_policy.identify(request)
     if identity is None:
         return None  # non-registered user has None user_id
-    user_id = yield from autz_policy.authorized_userid(identity)
+    user_id = await autz_policy.authorized_userid(identity)
     return user_id
 
 
-@asyncio.coroutine
-def permits(request, permission, context=None):
+async def permits(request, permission, context=None):
     assert isinstance(permission, (str, enum.Enum)), permission
     assert permission
     identity_policy = request.app.get(IDENTITY_KEY)
     autz_policy = request.app.get(AUTZ_KEY)
     if identity_policy is None or autz_policy is None:
         return True
-    identity = yield from identity_policy.identify(request)
+    identity = await identity_policy.identify(request)
     # non-registered user still may has some permissions
-    access = yield from autz_policy.permits(identity, permission, context)
+    access = await autz_policy.permits(identity, permission, context)
     return access
 
 
-@asyncio.coroutine
-def is_anonymous(request):
+async def is_anonymous(request):
     """Check if user is anonymous.
 
     User is considered anonymous if there is not identity
@@ -86,7 +80,7 @@ def is_anonymous(request):
     identity_policy = request.app.get(IDENTITY_KEY)
     if identity_policy is None:
         return True
-    identity = yield from identity_policy.identify(request)
+    identity = await identity_policy.identify(request)
     if identity is None:
         return True
     return False
@@ -98,9 +92,8 @@ def login_required(fn):
     User is considered authorized if authorized_userid
     returns some value.
     """
-    @asyncio.coroutine
     @wraps(fn)
-    def wrapped(*args, **kwargs):
+    async def wrapped(*args, **kwargs):
         request = args[-1]
         if not isinstance(request, web.BaseRequest):
             msg = ("Incorrect decorator usage. "
@@ -108,11 +101,11 @@ def login_required(fn):
                    "or `def handler(self, request)`.")
             raise RuntimeError(msg)
 
-        userid = yield from authorized_userid(request)
+        userid = await authorized_userid(request)
         if userid is None:
             raise web.HTTPUnauthorized
 
-        ret = yield from fn(*args, **kwargs)
+        ret = await fn(*args, **kwargs)
         return ret
 
     return wrapped
@@ -130,9 +123,8 @@ def has_permission(
     raises HTTPForbidden.
     """
     def wrapper(fn):
-        @asyncio.coroutine
         @wraps(fn)
-        def wrapped(*args, **kwargs):
+        async def wrapped(*args, **kwargs):
             request = args[-1]
             if not isinstance(request, web.BaseRequest):
                 msg = ("Incorrect decorator usage. "
@@ -140,14 +132,14 @@ def has_permission(
                        "or `def handler(self, request)`.")
                 raise RuntimeError(msg)
 
-            userid = yield from authorized_userid(request)
+            userid = await authorized_userid(request)
             if userid is None:
                 raise web.HTTPUnauthorized
 
-            allowed = yield from permits(request, permission, context)
+            allowed = await permits(request, permission, context)
             if not allowed:
                 raise web.HTTPForbidden
-            ret = yield from fn(*args, **kwargs)
+            ret = await fn(*args, **kwargs)
             return ret
 
         return wrapped
diff --git a/aiohttp_security/cookies_identity.py b/aiohttp_security/cookies_identity.py
index 807af34..3822ef9 100644
--- a/aiohttp_security/cookies_identity.py
+++ b/aiohttp_security/cookies_identity.py
@@ -5,8 +5,6 @@ more handy.
 
 """
 
-import asyncio
-
 from .abc import AbstractIdentityPolicy
 
 
@@ -19,19 +17,16 @@ class CookiesIdentityPolicy(AbstractIdentityPolicy):
         self._cookie_name = 'AIOHTTP_SECURITY'
         self._max_age = 30 * 24 * 3600
 
-    @asyncio.coroutine
-    def identify(self, request):
+    async def identify(self, request):
         identity = request.cookies.get(self._cookie_name)
         return identity
 
-    @asyncio.coroutine
-    def remember(self, request, response, identity, max_age=sentinel,
-                 **kwargs):
+    async def remember(self, request, response, identity, max_age=sentinel,
+                       **kwargs):
         if max_age is sentinel:
             max_age = self._max_age
         response.set_cookie(self._cookie_name, identity,
                             max_age=max_age, **kwargs)
 
-    @asyncio.coroutine
-    def forget(self, request, response):
+    async def forget(self, request, response):
         response.del_cookie(self._cookie_name)
diff --git a/aiohttp_security/session_identity.py b/aiohttp_security/session_identity.py
index 5d9cea0..13d197a 100644
--- a/aiohttp_security/session_identity.py
+++ b/aiohttp_security/session_identity.py
@@ -4,8 +4,6 @@ aiohttp_session.setup() should be called on application initialization
 to configure aiohttp_session properly.
 """
 
-import asyncio
-
 try:
     from aiohttp_session import get_session
     HAS_AIOHTTP_SESSION = True
@@ -24,17 +22,14 @@ class SessionIdentityPolicy(AbstractIdentityPolicy):
             raise ImportError(
                 'SessionIdentityPolicy requires `aiohttp_session`')
 
-    @asyncio.coroutine
-    def identify(self, request):
-        session = yield from get_session(request)
+    async def identify(self, request):
+        session = await get_session(request)
         return session.get(self._session_key)
 
-    @asyncio.coroutine
-    def remember(self, request, response, identity, **kwargs):
-        session = yield from get_session(request)
+    async def remember(self, request, response, identity, **kwargs):
+        session = await get_session(request)
         session[self._session_key] = identity
 
-    @asyncio.coroutine
-    def forget(self, request, response):
-        session = yield from get_session(request)
+    async def forget(self, request, response):
+        session = await get_session(request)
         session.pop(self._session_key, None)
diff --git a/demo/database_auth/db_auth.py b/demo/database_auth/db_auth.py
index bb9897c..82c29bd 100644
--- a/demo/database_auth/db_auth.py
+++ b/demo/database_auth/db_auth.py
@@ -1,5 +1,3 @@
-import asyncio
-
 import sqlalchemy as sa
 
 from aiohttp_security.abc import AbstractAuthorizationPolicy
@@ -12,29 +10,27 @@ class DBAuthorizationPolicy(AbstractAuthorizationPolicy):
     def __init__(self, dbengine):
         self.dbengine = dbengine
 
-    @asyncio.coroutine
     def authorized_userid(self, identity):
-        with (yield from self.dbengine) as conn:
+        async with self.dbengine as conn:
             where = sa.and_(db.users.c.login == identity,
                             sa.not_(db.users.c.disabled))
             query = db.users.count().where(where)
-            ret = yield from conn.scalar(query)
+            ret = await conn.scalar(query)
             if ret:
                 return identity
             else:
                 return None
 
-    @asyncio.coroutine
-    def permits(self, identity, permission, context=None):
+    async def permits(self, identity, permission, context=None):
         if identity is None:
             return False
 
-        with (yield from self.dbengine) as conn:
+        async with self.dbengine as conn:
             where = sa.and_(db.users.c.login == identity,
                             sa.not_(db.users.c.disabled))
             query = db.users.select().where(where)
-            ret = yield from conn.execute(query)
-            user = yield from ret.fetchone()
+            ret = await conn.execute(query)
+            user = await ret.fetchone()
             if user is not None:
                 user_id = user[0]
                 is_superuser = user[3]
@@ -43,8 +39,8 @@ class DBAuthorizationPolicy(AbstractAuthorizationPolicy):
 
                 where = db.permissions.c.user_id == user_id
                 query = db.permissions.select().where(where)
-                ret = yield from conn.execute(query)
-                result = yield from ret.fetchall()
+                ret = await conn.execute(query)
+                result = await ret.fetchall()
                 if ret is not None:
                     for record in result:
                         if record.perm_name == permission:
@@ -53,14 +49,13 @@ class DBAuthorizationPolicy(AbstractAuthorizationPolicy):
             return False
 
 
-@asyncio.coroutine
-def check_credentials(db_engine, username, password):
-    with (yield from db_engine) as conn:
+async def check_credentials(db_engine, username, password):
+    async with db_engine as conn:
         where = sa.and_(db.users.c.login == username,
                         sa.not_(db.users.c.disabled))
         query = db.users.select().where(where)
-        ret = yield from conn.execute(query)
-        user = yield from ret.fetchone()
+        ret = await conn.execute(query)
+        user = await ret.fetchone()
         if user is not None:
             hash = user[2]
             return sha256_crypt.verify(password, hash)
diff --git a/demo/database_auth/handlers.py b/demo/database_auth/handlers.py
index 3de2aed..b0c6658 100644
--- a/demo/database_auth/handlers.py
+++ b/demo/database_auth/handlers.py
@@ -1,4 +1,3 @@
-import asyncio
 import functools
 
 from aiohttp import web
@@ -10,14 +9,13 @@ from .db_auth import check_credentials
 
 def require(permission):
     def wrapper(f):
-        @asyncio.coroutine
         @functools.wraps(f)
-        def wrapped(self, request):
-            has_perm = yield from permits(request, permission)
+        async def wrapped(self, request):
+            has_perm = await permits(request, permission)
             if not has_perm:
                 message = 'User has no permission {}'.format(permission)
                 raise web.HTTPForbidden(body=message.encode())
-            return (yield from f(self, request))
+            return await f(self, request)
         return wrapped
     return wrapper
 
@@ -40,9 +38,8 @@ class Web(object):
 </body>
 """
 
-    @asyncio.coroutine
-    def index(self, request):
-        username = yield from authorized_userid(request)
+    async def index(self, request):
+        username = await authorized_userid(request)
         if username:
             template = self.index_template.format(
                 message='Hello, {username}!'.format(username=username))
@@ -51,37 +48,33 @@ class Web(object):
         response = web.Response(body=template.encode())
         return response
 
-    @asyncio.coroutine
-    def login(self, request):
+    async def login(self, request):
         response = web.HTTPFound('/')
-        form = yield from request.post()
+        form = await request.post()
         login = form.get('login')
         password = form.get('password')
         db_engine = request.app.db_engine
-        if (yield from check_credentials(db_engine, login, password)):
-            yield from remember(request, response, login)
+        if await check_credentials(db_engine, login, password):
+            await remember(request, response, login)
             return response
 
         return web.HTTPUnauthorized(
             body=b'Invalid username/password combination')
 
     @require('public')
-    @asyncio.coroutine
-    def logout(self, request):
+    async def logout(self, request):
         response = web.Response(body=b'You have been logged out')
-        yield from forget(request, response)
+        await forget(request, response)
         return response
 
     @require('public')
-    @asyncio.coroutine
-    def internal_page(self, request):
+    async def internal_page(self, request):
         response = web.Response(
             body=b'This page is visible for all registered users')
         return response
 
     @require('protected')
-    @asyncio.coroutine
-    def protected_page(self, request):
+    async def protected_page(self, request):
         response = web.Response(body=b'You are on protected page')
         return response
 
diff --git a/demo/database_auth/main.py b/demo/database_auth/main.py
index 4fb3211..a2a6be8 100644
--- a/demo/database_auth/main.py
+++ b/demo/database_auth/main.py
@@ -13,10 +13,9 @@ from demo.db_auth import DBAuthorizationPolicy
 from demo.handlers import Web
 
 
-@asyncio.coroutine
 def init(loop):
-    redis_pool = yield from create_pool(('localhost', 6379))
-    db_engine = yield from create_engine(user='aiohttp_security',
+    redis_pool = await create_pool(('localhost', 6379))
+    db_engine = await create_engine(user='aiohttp_security',
                                          password='aiohttp_security',
                                          database='aiohttp_security',
                                          host='127.0.0.1')
@@ -31,21 +30,20 @@ def init(loop):
     web_handlers.configure(app)
 
     handler = app.make_handler()
-    srv = yield from loop.create_server(handler, '127.0.0.1', 8080)
+    srv = await loop.create_server(handler, '127.0.0.1', 8080)
     print('Server started at http://127.0.0.1:8080')
     return srv, app, handler
 
 
-@asyncio.coroutine
-def finalize(srv, app, handler):
+async def finalize(srv, app, handler):
     sock = srv.sockets[0]
     app.loop.remove_reader(sock.fileno())
     sock.close()
 
-    yield from handler.finish_connections(1.0)
+    await handler.finish_connections(1.0)
     srv.close()
-    yield from srv.wait_closed()
-    yield from app.finish()
+    await srv.wait_closed()
+    await app.finish()
 
 
 def main():
diff --git a/demo/dictionary_auth/handlers.py b/demo/dictionary_auth/handlers.py
index 2dffe55..a0d7f7c 100644
--- a/demo/dictionary_auth/handlers.py
+++ b/demo/dictionary_auth/handlers.py
@@ -1,4 +1,3 @@
-import asyncio
 import functools
 from textwrap import dedent
 
@@ -11,14 +10,13 @@ from .authz import check_credentials
 
 def require(permission):
     def wrapper(f):
-        @asyncio.coroutine
         @functools.wraps(f)
-        def wrapped(request):
-            has_perm = yield from permits(request, permission)
+        async def wrapped(request):
+            has_perm = await permits(request, permission)
             if not has_perm:
                 message = 'User has no permission {}'.format(permission)
                 raise web.HTTPForbidden(body=message.encode())
-            return (yield from f(request))
+            return await f(request)
         return wrapped
     return wrapper
 
diff --git a/docs/example.rst b/docs/example.rst
index e50ba38..7498add 100644
--- a/docs/example.rst
+++ b/docs/example.rst
@@ -10,16 +10,14 @@ Simple example::
     import asyncio
     from aiohttp import web
 
-    @asyncio.coroutine
-    def root_handler(request):
+    async def root_handler(request):
         text = "Alive and kicking!"
         return web.Response(body=text.encode('utf-8'))
 
     # option 2: auth at a higher level?
     # set user_id and allowed in the wsgi handler
     @protect('view_user')
-    @asyncio.coroutine
-    def user_handler(request):
+    async def user_handler(request):
         name = request.match_info.get('name', "Anonymous")
         text = "Hello, " + name
         return web.Response(body=text.encode('utf-8'))
@@ -27,14 +25,12 @@ Simple example::
 
     # option 3: super low
     # wsgi doesn't do anything
-    @asyncio.coroutine
-    def user_update_handler(request):
+    async def user_update_handler(request):
         # identity, asked_permission
-        user_id = yield from identity_policy.identify(request)
-        identity = yield from auth_policy.authorized_user_id(user_id)
-        allowed = yield from request.auth_policy.permits(
-                identity, asked_permission
-                )
+        user_id = await identity_policy.identify(request)
+        identity = await auth_policy.authorized_user_id(user_id)
+        allowed = await request.auth_policy.permits(identity,
+                                                    asked_permission)
         if not allowed:
             # how is this pluggable as well?
             # ? return NotAllowedStream()
@@ -42,8 +38,7 @@ Simple example::
 
         update_user()
 
-    @asyncio.coroutine
-    def init(loop):
+    async def init(loop):
         # set up identity and auth
         auth_policy = DictionaryAuthorizationPolicy({'me': ('view_user',),
                                                      'you': ('view_user',
@@ -60,7 +55,7 @@ Simple example::
         app.router.add_route('GET', '/{user}/edit', user_update_handler)
 
         # get it started
-        srv = yield from loop.create_server(app.make_handler(),
+        srv = await loop.create_server(app.make_handler(),
                                             '127.0.0.1', 8080)
         print("Server started at http://127.0.0.1:8080")
         return srv
diff --git a/docs/example_db_auth.rst b/docs/example_db_auth.rst
index 70b78e4..ec87685 100644
--- a/docs/example_db_auth.rst
+++ b/docs/example_db_auth.rst
@@ -67,13 +67,12 @@ In our example we will lookup database by user login and if present return
 this identity::
 
 
-    @asyncio.coroutine
-    def authorized_userid(self, identity):
-        with (yield from self.dbengine) as conn:
+    async def authorized_userid(self, identity):
+        async with  self.dbengine as conn:
             where = sa.and_(db.users.c.login == identity,
                             sa.not_(db.users.c.disabled))
             query = db.users.count().where(where)
-            ret = yield from conn.scalar(query)
+            ret = await conn.scalar(query)
             if ret:
                 return identity
             else:
@@ -84,17 +83,16 @@ For permission check we will fetch the user first, check if he is superuser
 (all permissions are allowed), otherwise check if permission is explicitly set
 for that user::
 
-    @asyncio.coroutine
-    def permits(self, identity, permission, context=None):
+    async def permits(self, identity, permission, context=None):
         if identity is None:
             return False
 
-        with (yield from self.dbengine) as conn:
+        async with self.dbengine as conn:
             where = sa.and_(db.users.c.login == identity,
                             sa.not_(db.users.c.disabled))
             query = db.users.select().where(where)
-            ret = yield from conn.execute(query)
-            user = yield from ret.fetchone()
+            ret = await conn.execute(query)
+            user = await ret.fetchone()
             if user is not None:
                 user_id = user[0]
                 is_superuser = user[4]
@@ -103,8 +101,8 @@ for that user::
 
                 where = db.permissions.c.user_id == user_id
                 query = db.permissions.select().where(where)
-                ret = yield from conn.execute(query)
-                result = yield from ret.fetchall()
+                ret = await conn.execute(query)
+                result = await ret.fetchall()
                 if ret is not None:
                     for record in result:
                         if record.perm_name == permission:
@@ -127,13 +125,12 @@ Once we have all the code in place we can install it for our application::
     from .db_auth import DBAuthorizationPolicy
 
 
-    @asyncio.coroutine
-    def init(loop):
-        redis_pool = yield from create_pool(('localhost', 6379))
-        dbengine = yield from create_engine(user='aiohttp_security',
-                                            password='aiohttp_security',
-                                            database='aiohttp_security',
-                                            host='127.0.0.1')
+    async def init(loop):
+        redis_pool = await create_pool(('localhost', 6379))
+        dbengine = await create_engine(user='aiohttp_security',
+                                       password='aiohttp_security',
+                                       database='aiohttp_security',
+                                       host='127.0.0.1')
         app = web.Application(loop=loop)
         setup_session(app, RedisStorage(redis_pool))
         setup_security(app,
@@ -148,14 +145,13 @@ help to do that::
 
     def require(permission):
         def wrapper(f):
-            @asyncio.coroutine
             @functools.wraps(f)
-            def wrapped(self, request):
-                has_perm = yield from permits(request, permission)
+            async def wrapped(self, request):
+                has_perm = await permits(request, permission)
                 if not has_perm:
                     message = 'User has no permission {}'.format(permission)
                     raise web.HTTPForbidden(body=message.encode())
-                return (yield from f(self, request))
+                return await f(self, request)
             return wrapped
         return wrapper
 
@@ -164,8 +160,7 @@ For each view you need to protect just apply the decorator on it::
 
     class Web:
         @require('protected')
-        @asyncio.coroutine
-        def protected_page(self, request):
+        async def protected_page(self, request):
             response = web.Response(body=b'You are on protected page')
             return response
 
@@ -187,14 +182,13 @@ function may do what you trying to accomplish::
 
     from passlib.hash import sha256_crypt
 
-    @asyncio.coroutine
-    def check_credentials(db_engine, username, password):
-        with (yield from db_engine) as conn:
+    async def check_credentials(db_engine, username, password):
+        async with  db_engine as conn:
             where = sa.and_(db.users.c.login == username,
                             sa.not_(db.users.c.disabled))
             query = db.users.select().where(where)
-            ret = yield from conn.execute(query)
-            user = yield from ret.fetchone()
+            ret = await conn.execute(query)
+            user = await ret.fetchone()
             if user is not None:
                 hash = user[2]
                 return sha256_crypt.verify(password, hash)
diff --git a/tests/test_cookies_identity.py b/tests/test_cookies_identity.py
index e197751..8d739e6 100644
--- a/tests/test_cookies_identity.py
+++ b/tests/test_cookies_identity.py
@@ -1,5 +1,3 @@
-import asyncio
-
 from aiohttp import web
 from aiohttp_security import (remember, forget,
                               AbstractAuthorizationPolicy)
@@ -10,47 +8,39 @@ from aiohttp_security.api import IDENTITY_KEY
 
 class Autz(AbstractAuthorizationPolicy):
 
-    @asyncio.coroutine
-    def permits(self, identity, permission, context=None):
+    async def permits(self, identity, permission, context=None):
         pass
 
-    @asyncio.coroutine
-    def authorized_userid(self, identity):
+    async def authorized_userid(self, identity):
         pass
 
 
-@asyncio.coroutine
-def test_remember(loop, test_client):
+async def test_remember(loop, test_client):
 
-    @asyncio.coroutine
-    def handler(request):
+    async def handler(request):
         response = web.Response()
-        yield from remember(request, response, 'Andrew')
+        await remember(request, response, 'Andrew')
         return response
 
     app = web.Application(loop=loop)
     _setup(app, CookiesIdentityPolicy(), Autz())
     app.router.add_route('GET', '/', handler)
-    client = yield from test_client(app)
-    resp = yield from client.get('/')
+    client = await test_client(app)
+    resp = await client.get('/')
     assert 200 == resp.status
     assert 'Andrew' == resp.cookies['AIOHTTP_SECURITY'].value
-    yield from resp.release()
 
 
-@asyncio.coroutine
-def test_identify(loop, test_client):
+async def test_identify(loop, test_client):
 
-    @asyncio.coroutine
-    def create(request):
+    async def create(request):
         response = web.Response()
-        yield from remember(request, response, 'Andrew')
+        await remember(request, response, 'Andrew')
         return response
 
-    @asyncio.coroutine
-    def check(request):
+    async def check(request):
         policy = request.app[IDENTITY_KEY]
-        user_id = yield from policy.identify(request)
+        user_id = await policy.identify(request)
         assert 'Andrew' == user_id
         return web.Response()
 
@@ -58,32 +48,27 @@ def test_identify(loop, test_client):
     _setup(app, CookiesIdentityPolicy(), Autz())
     app.router.add_route('GET', '/', check)
     app.router.add_route('POST', '/', create)
-    client = yield from test_client(app)
-    resp = yield from client.post('/')
+    client = await test_client(app)
+    resp = await client.post('/')
     assert 200 == resp.status
-    yield from resp.release()
-    resp = yield from client.get('/')
+    await resp.release()
+    resp = await client.get('/')
     assert 200 == resp.status
-    yield from resp.release()
 
 
-@asyncio.coroutine
-def test_forget(loop, test_client):
+async def test_forget(loop, test_client):
 
-    @asyncio.coroutine
-    def index(request):
+    async def index(request):
         return web.Response()
 
-    @asyncio.coroutine
-    def login(request):
+    async def login(request):
         response = web.HTTPFound(location='/')
-        yield from remember(request, response, 'Andrew')
+        await remember(request, response, 'Andrew')
         return response
 
-    @asyncio.coroutine
-    def logout(request):
+    async def logout(request):
         response = web.HTTPFound(location='/')
-        yield from forget(request, response)
+        await forget(request, response)
         return response
 
     app = web.Application(loop=loop)
@@ -91,18 +76,17 @@ def test_forget(loop, test_client):
     app.router.add_route('GET', '/', index)
     app.router.add_route('POST', '/login', login)
     app.router.add_route('POST', '/logout', logout)
-    client = yield from test_client(app)
-    resp = yield from client.post('/login')
+    client = await test_client(app)
+    resp = await client.post('/login')
     assert 200 == resp.status
     assert str(resp.url).endswith('/')
     cookies = client.session.cookie_jar.filter_cookies(
         client.make_url('/'))
     assert 'Andrew' == cookies['AIOHTTP_SECURITY'].value
-    yield from resp.release()
-    resp = yield from client.post('/logout')
+
+    resp = await client.post('/logout')
     assert 200 == resp.status
     assert str(resp.url).endswith('/')
     cookies = client.session.cookie_jar.filter_cookies(
         client.make_url('/'))
     assert 'AIOHTTP_SECURITY' not in cookies
-    yield from resp.release()
diff --git a/tests/test_dict_autz.py b/tests/test_dict_autz.py
index add2224..9151ce0 100644
--- a/tests/test_dict_autz.py
+++ b/tests/test_dict_autz.py
@@ -1,4 +1,3 @@
-import asyncio
 import enum
 
 from aiohttp import web
@@ -11,33 +10,28 @@ from aiohttp_security.cookies_identity import CookiesIdentityPolicy
 
 class Autz(AbstractAuthorizationPolicy):
 
-    @asyncio.coroutine
-    def permits(self, identity, permission, context=None):
+    async def permits(self, identity, permission, context=None):
         if identity == 'UserID':
             return permission in {'read', 'write'}
         else:
             return False
 
-    @asyncio.coroutine
-    def authorized_userid(self, identity):
+    async def authorized_userid(self, identity):
         if identity == 'UserID':
             return 'Andrew'
         else:
             return None
 
 
-@asyncio.coroutine
-def test_authorized_userid(loop, test_client):
+async def test_authorized_userid(loop, test_client):
 
-    @asyncio.coroutine
-    def login(request):
+    async def login(request):
         response = web.HTTPFound(location='/')
-        yield from remember(request, response, 'UserID')
+        await remember(request, response, 'UserID')
         return response
 
-    @asyncio.coroutine
-    def check(request):
-        userid = yield from authorized_userid(request)
+    async def check(request):
+        userid = await authorized_userid(request)
         assert 'Andrew' == userid
         return web.Response(text=userid)
 
@@ -45,36 +39,31 @@ def test_authorized_userid(loop, test_client):
     _setup(app, CookiesIdentityPolicy(), Autz())
     app.router.add_route('GET', '/', check)
     app.router.add_route('POST', '/login', login)
-    client = yield from test_client(app)
+    client = await test_client(app)
 
-    resp = yield from client.post('/login')
+    resp = await client.post('/login')
     assert 200 == resp.status
-    txt = yield from resp.text()
+    txt = await resp.text()
     assert 'Andrew' == txt
-    yield from resp.release()
 
 
-@asyncio.coroutine
-def test_authorized_userid_not_authorized(loop, test_client):
+async def test_authorized_userid_not_authorized(loop, test_client):
 
-    @asyncio.coroutine
-    def check(request):
-        userid = yield from authorized_userid(request)
+    async def check(request):
+        userid = await authorized_userid(request)
         assert userid is None
         return web.Response()
 
     app = web.Application(loop=loop)
     _setup(app, CookiesIdentityPolicy(), Autz())
     app.router.add_route('GET', '/', check)
-    client = yield from test_client(app)
+    client = await test_client(app)
 
-    resp = yield from client.get('/')
+    resp = await client.get('/')
     assert 200 == resp.status
-    yield from resp.release()
 
 
-@asyncio.coroutine
-def test_permits_enum_permission(loop, test_client):
+async def test_permits_enum_permission(loop, test_client):
     class Permission(enum.Enum):
         READ = '101'
         WRITE = '102'
@@ -82,33 +71,29 @@ def test_permits_enum_permission(loop, test_client):
 
     class Autz(AbstractAuthorizationPolicy):
 
-        @asyncio.coroutine
-        def permits(self, identity, permission, context=None):
+        async def permits(self, identity, permission, context=None):
             if identity == 'UserID':
                 return permission in {Permission.READ, Permission.WRITE}
             else:
                 return False
 
-        @asyncio.coroutine
-        def authorized_userid(self, identity):
+        async def authorized_userid(self, identity):
             if identity == 'UserID':
                 return 'Andrew'
             else:
                 return None
 
-    @asyncio.coroutine
-    def login(request):
+    async def login(request):
         response = web.HTTPFound(location='/')
-        yield from remember(request, response, 'UserID')
+        await remember(request, response, 'UserID')
         return response
 
-    @asyncio.coroutine
-    def check(request):
-        ret = yield from permits(request, Permission.READ)
+    async def check(request):
+        ret = await permits(request, Permission.READ)
         assert ret
-        ret = yield from permits(request, Permission.WRITE)
+        ret = await permits(request, Permission.WRITE)
         assert ret
-        ret = yield from permits(request, Permission.UNKNOWN)
+        ret = await permits(request, Permission.UNKNOWN)
         assert not ret
         return web.Response()
 
@@ -116,54 +101,46 @@ def test_permits_enum_permission(loop, test_client):
     _setup(app, CookiesIdentityPolicy(), Autz())
     app.router.add_route('GET', '/', check)
     app.router.add_route('POST', '/login', login)
-    client = yield from test_client(app)
-    resp = yield from client.post('/login')
+    client = await test_client(app)
+    resp = await client.post('/login')
     assert 200 == resp.status
-    yield from resp.release()
 
 
-@asyncio.coroutine
-def test_permits_unauthorized(loop, test_client):
+async def test_permits_unauthorized(loop, test_client):
 
-    @asyncio.coroutine
-    def check(request):
-        ret = yield from permits(request, 'read')
+    async def check(request):
+        ret = await permits(request, 'read')
         assert not ret
-        ret = yield from permits(request, 'write')
+        ret = await permits(request, 'write')
         assert not ret
-        ret = yield from permits(request, 'unknown')
+        ret = await permits(request, 'unknown')
         assert not ret
         return web.Response()
 
     app = web.Application(loop=loop)
     _setup(app, CookiesIdentityPolicy(), Autz())
     app.router.add_route('GET', '/', check)
-    client = yield from test_client(app)
-    resp = yield from client.get('/')
+    client = await test_client(app)
+    resp = await client.get('/')
     assert 200 == resp.status
-    yield from resp.release()
 
 
-@asyncio.coroutine
-def test_is_anonymous(loop, test_client):
+async def test_is_anonymous(loop, test_client):
 
-    @asyncio.coroutine
-    def index(request):
-        is_anon = yield from is_anonymous(request)
+    async def index(request):
+        is_anon = await is_anonymous(request)
         if is_anon:
             return web.HTTPUnauthorized()
         return web.HTTPOk()
 
-    @asyncio.coroutine
-    def login(request):
+    async def login(request):
         response = web.HTTPFound(location='/')
-        yield from remember(request, response, 'UserID')
+        await remember(request, response, 'UserID')
         return response
 
-    @asyncio.coroutine
-    def logout(request):
+    async def logout(request):
         response = web.HTTPFound(location='/')
-        yield from forget(request, response)
+        await forget(request, response)
         return response
 
     app = web.Application(loop=loop)
@@ -171,36 +148,32 @@ def test_is_anonymous(loop, test_client):
     app.router.add_route('GET', '/', index)
     app.router.add_route('POST', '/login', login)
     app.router.add_route('POST', '/logout', logout)
-    client = yield from test_client(app)
-    resp = yield from client.get('/')
+    client = await test_client(app)
+    resp = await client.get('/')
     assert web.HTTPUnauthorized.status_code == resp.status
 
-    yield from client.post('/login')
-    resp = yield from client.get('/')
+    await client.post('/login')
+    resp = await client.get('/')
     assert web.HTTPOk.status_code == resp.status
 
-    yield from client.post('/logout')
-    resp = yield from client.get('/')
+    await client.post('/logout')
+    resp = await client.get('/')
     assert web.HTTPUnauthorized.status_code == resp.status
 
 
-@asyncio.coroutine
-def test_login_required(loop, test_client):
+async def test_login_required(loop, test_client):
     @login_required
-    @asyncio.coroutine
-    def index(request):
+    async def index(request):
         return web.HTTPOk()
 
-    @asyncio.coroutine
-    def login(request):
+    async def login(request):
         response = web.HTTPFound(location='/')
-        yield from remember(request, response, 'UserID')
+        await remember(request, response, 'UserID')
         return response
 
-    @asyncio.coroutine
-    def logout(request):
+    async def logout(request):
         response = web.HTTPFound(location='/')
-        yield from forget(request, response)
+        await forget(request, response)
         return response
 
     app = web.Application(loop=loop)
@@ -208,47 +181,41 @@ def test_login_required(loop, test_client):
     app.router.add_route('GET', '/', index)
     app.router.add_route('POST', '/login', login)
     app.router.add_route('POST', '/logout', logout)
-    client = yield from test_client(app)
-    resp = yield from client.get('/')
+    client = await test_client(app)
+    resp = await client.get('/')
     assert web.HTTPUnauthorized.status_code == resp.status
 
-    yield from client.post('/login')
-    resp = yield from client.get('/')
+    await client.post('/login')
+    resp = await client.get('/')
     assert web.HTTPOk.status_code == resp.status
 
-    yield from client.post('/logout')
-    resp = yield from client.get('/')
+    await client.post('/logout')
+    resp = await client.get('/')
     assert web.HTTPUnauthorized.status_code == resp.status
 
 
-@asyncio.coroutine
-def test_has_permission(loop, test_client):
+async def test_has_permission(loop, test_client):
 
     @has_permission('read')
-    @asyncio.coroutine
-    def index_read(request):
+    async def index_read(request):
         return web.HTTPOk()
 
     @has_permission('write')
-    @asyncio.coroutine
-    def index_write(request):
+    async def index_write(request):
         return web.HTTPOk()
 
     @has_permission('forbid')
-    @asyncio.coroutine
-    def index_forbid(request):
+    async def index_forbid(request):
         return web.HTTPOk()
 
-    @asyncio.coroutine
-    def login(request):
+    async def login(request):
         response = web.HTTPFound(location='/')
-        yield from remember(request, response, 'UserID')
+        await remember(request, response, 'UserID')
         return response
 
-    @asyncio.coroutine
-    def logout(request):
+    async def logout(request):
         response = web.HTTPFound(location='/')
-        yield from forget(request, response)
+        await forget(request, response)
         return response
 
     app = web.Application(loop=loop)
@@ -258,27 +225,27 @@ def test_has_permission(loop, test_client):
     app.router.add_route('GET', '/permission/forbid', index_forbid)
     app.router.add_route('POST', '/login', login)
     app.router.add_route('POST', '/logout', logout)
-    client = yield from test_client(app)
+    client = await test_client(app)
 
-    resp = yield from client.get('/permission/read')
+    resp = await client.get('/permission/read')
     assert web.HTTPUnauthorized.status_code == resp.status
-    resp = yield from client.get('/permission/write')
+    resp = await client.get('/permission/write')
     assert web.HTTPUnauthorized.status_code == resp.status
-    resp = yield from client.get('/permission/forbid')
+    resp = await client.get('/permission/forbid')
     assert web.HTTPUnauthorized.status_code == resp.status
 
-    yield from client.post('/login')
-    resp = yield from client.get('/permission/read')
+    await client.post('/login')
+    resp = await client.get('/permission/read')
     assert web.HTTPOk.status_code == resp.status
-    resp = yield from client.get('/permission/write')
+    resp = await client.get('/permission/write')
     assert web.HTTPOk.status_code == resp.status
-    resp = yield from client.get('/permission/forbid')
+    resp = await client.get('/permission/forbid')
     assert web.HTTPForbidden.status_code == resp.status
 
-    yield from client.post('/logout')
-    resp = yield from client.get('/permission/read')
+    await client.post('/logout')
+    resp = await client.get('/permission/read')
     assert web.HTTPUnauthorized.status_code == resp.status
-    resp = yield from client.get('/permission/write')
+    resp = await client.get('/permission/write')
     assert web.HTTPUnauthorized.status_code == resp.status
-    resp = yield from client.get('/permission/forbid')
+    resp = await client.get('/permission/forbid')
     assert web.HTTPUnauthorized.status_code == resp.status
diff --git a/tests/test_no_auth.py b/tests/test_no_auth.py
index cdc448e..55051df 100644
--- a/tests/test_no_auth.py
+++ b/tests/test_no_auth.py
@@ -1,42 +1,34 @@
-import asyncio
-
 from aiohttp import web
 from aiohttp_security import authorized_userid, permits
 
 
-@asyncio.coroutine
-def test_authorized_userid(loop, test_client):
+async def test_authorized_userid(loop, test_client):
 
-    @asyncio.coroutine
-    def check(request):
-        userid = yield from authorized_userid(request)
+    async def check(request):
+        userid = await authorized_userid(request)
         assert userid is None
         return web.Response()
 
     app = web.Application(loop=loop)
     app.router.add_route('GET', '/', check)
-    client = yield from test_client(app)
-    resp = yield from client.get('/')
+    client = await test_client(app)
+    resp = await client.get('/')
     assert 200 == resp.status
-    yield from resp.release()
 
 
-@asyncio.coroutine
-def test_permits(loop, test_client):
+async def test_permits(loop, test_client):
 
-    @asyncio.coroutine
-    def check(request):
-        ret = yield from permits(request, 'read')
+    async def check(request):
+        ret = await permits(request, 'read')
         assert ret
-        ret = yield from permits(request, 'write')
+        ret = await permits(request, 'write')
         assert ret
-        ret = yield from permits(request, 'unknown')
+        ret = await permits(request, 'unknown')
         assert ret
         return web.Response()
 
     app = web.Application(loop=loop)
     app.router.add_route('GET', '/', check)
-    client = yield from test_client(app)
-    resp = yield from client.get('/')
+    client = await test_client(app)
+    resp = await client.get('/')
     assert 200 == resp.status
-    yield from resp.release()
diff --git a/tests/test_no_identity.py b/tests/test_no_identity.py
index c2bd329..208521b 100644
--- a/tests/test_no_identity.py
+++ b/tests/test_no_identity.py
@@ -1,42 +1,34 @@
-import asyncio
-
 from aiohttp import web
 from aiohttp_security import remember, forget
 
 
-@asyncio.coroutine
-def test_remember(loop, test_client):
+async def test_remember(loop, test_client):
 
-    @asyncio.coroutine
-    def do_remember(request):
+    async def do_remember(request):
         response = web.Response()
-        yield from remember(request, response, 'Andrew')
+        await remember(request, response, 'Andrew')
 
     app = web.Application(loop=loop)
     app.router.add_route('POST', '/', do_remember)
-    client = yield from test_client(app)
-    resp = yield from client.post('/')
+    client = await test_client(app)
+    resp = await client.post('/')
     assert 500 == resp.status
     assert (('Security subsystem is not initialized, '
              'call aiohttp_security.setup(...) first') ==
             resp.reason)
-    yield from resp.release()
 
 
-@asyncio.coroutine
-def test_forget(loop, test_client):
+async def test_forget(loop, test_client):
 
-    @asyncio.coroutine
-    def do_forget(request):
+    async def do_forget(request):
         response = web.Response()
-        yield from forget(request, response)
+        await forget(request, response)
 
     app = web.Application(loop=loop)
     app.router.add_route('POST', '/', do_forget)
-    client = yield from test_client(app)
-    resp = yield from client.post('/')
+    client = await test_client(app)
+    resp = await client.post('/')
     assert 500 == resp.status
     assert (('Security subsystem is not initialized, '
              'call aiohttp_security.setup(...) first') ==
             resp.reason)
-    yield from resp.release()
diff --git a/tests/test_session_identity.py b/tests/test_session_identity.py
index 86f9cf7..d6377a8 100644
--- a/tests/test_session_identity.py
+++ b/tests/test_session_identity.py
@@ -1,4 +1,3 @@
-import asyncio
 import pytest
 
 from aiohttp import web
@@ -13,12 +12,10 @@ from aiohttp_session import setup as setup_session
 
 class Autz(AbstractAuthorizationPolicy):
 
-    @asyncio.coroutine
-    def permits(self, identity, permission, context=None):
+    async def permits(self, identity, permission, context=None):
         pass
 
-    @asyncio.coroutine
-    def authorized_userid(self, identity):
+    async def authorized_userid(self, identity):
         pass
 
 
@@ -30,81 +27,67 @@ def make_app(loop):
     return app
 
 
-@asyncio.coroutine
-def test_remember(make_app, test_client):
+async def test_remember(make_app, test_client):
 
-    @asyncio.coroutine
-    def handler(request):
+    async def handler(request):
         response = web.Response()
-        yield from remember(request, response, 'Andrew')
+        await remember(request, response, 'Andrew')
         return response
 
-    @asyncio.coroutine
-    def check(request):
-        session = yield from get_session(request)
+    async def check(request):
+        session = await get_session(request)
         assert session['AIOHTTP_SECURITY'] == 'Andrew'
         return web.HTTPOk()
 
     app = make_app()
     app.router.add_route('GET', '/', handler)
     app.router.add_route('GET', '/check', check)
-    client = yield from test_client(app)
-    resp = yield from client.get('/')
+    client = await test_client(app)
+    resp = await client.get('/')
     assert 200 == resp.status
-    yield from resp.release()
 
-    resp = yield from client.get('/check')
+    resp = await client.get('/check')
     assert 200 == resp.status
-    yield from resp.release()
 
 
-@asyncio.coroutine
-def test_identify(make_app, test_client):
+async def test_identify(make_app, test_client):
 
-    @asyncio.coroutine
-    def create(request):
+    async def create(request):
         response = web.Response()
-        yield from remember(request, response, 'Andrew')
+        await remember(request, response, 'Andrew')
         return response
 
-    @asyncio.coroutine
-    def check(request):
+    async def check(request):
         policy = request.app[IDENTITY_KEY]
-        user_id = yield from policy.identify(request)
+        user_id = await policy.identify(request)
         assert 'Andrew' == user_id
         return web.Response()
 
     app = make_app()
     app.router.add_route('GET', '/', check)
     app.router.add_route('POST', '/', create)
-    client = yield from test_client(app)
-    resp = yield from client.post('/')
+    client = await test_client(app)
+    resp = await client.post('/')
     assert 200 == resp.status
-    yield from resp.release()
 
-    resp = yield from client.get('/')
+    resp = await client.get('/')
     assert 200 == resp.status
-    yield from resp.release()
 
 
-@asyncio.coroutine
-def test_forget(make_app, test_client):
+async def test_forget(make_app, test_client):
 
-    @asyncio.coroutine
-    def index(request):
-        session = yield from get_session(request)
+    async def index(request):
+        session = await get_session(request)
         return web.HTTPOk(text=session.get('AIOHTTP_SECURITY', ''))
 
-    @asyncio.coroutine
-    def login(request):
+    async def login(request):
         response = web.HTTPFound(location='/')
-        yield from remember(request, response, 'Andrew')
+        await remember(request, response, 'Andrew')
         return response
 
-    @asyncio.coroutine
-    def logout(request):
+    async def logout(request):
         response = web.HTTPFound('/')
-        yield from forget(request, response)
+        await forget(request, response)
         return response
 
     app = make_app()
@@ -112,18 +95,16 @@ def test_forget(make_app, test_client):
     app.router.add_route('POST', '/login', login)
     app.router.add_route('POST', '/logout', logout)
 
-    client = yield from test_client(app)
+    client = await test_client(app)
 
-    resp = yield from client.post('/login')
+    resp = await client.post('/login')
     assert 200 == resp.status
     assert str(resp.url).endswith('/')
-    txt = yield from resp.text()
+    txt = await resp.text()
     assert 'Andrew' == txt
-    yield from resp.release()
 
-    resp = yield from client.post('/logout')
+    resp = await client.post('/logout')
     assert 200 == resp.status
     assert str(resp.url).endswith('/')
-    txt = yield from resp.text()
+    txt = await resp.text()
     assert '' == txt
-    yield from resp.release()