Repository: aio-libs/aiohttp

Issue:
Drop await aiohttp.request(...)
Closing request/session is too error-prone in the case, especially if an exception was generated.

The only robust solution is prohibiting `await request()` syntax but supporting `async with request() as resp:` only.

After implementing proposed restriction we'll get very clean context and behavior.
Drop await aiohttp.request(...)
Closing request/session is too error-prone in the case, especially if an exception was generated.

The only robust solution is prohibiting `await request()` syntax but supporting `async with request() as resp:` only.

After implementing proposed restriction we'll get very clean context and behavior.


Discussion:

Resolving patch:
diff --git a/aiohttp/client.py b/aiohttp/client.py
--- a/aiohttp/client.py
+++ b/aiohttp/client.py
@@ -8,6 +8,7 @@
 import sys
 import traceback
 import warnings
+from collections.abc import Coroutine
 
 from multidict import CIMultiDict, MultiDict, MultiDictProxy, istr
 from yarl import URL
@@ -23,8 +24,7 @@
 from .connector import *  # noqa
 from .connector import TCPConnector
 from .cookiejar import CookieJar
-from .helpers import (CeilTimeout, TimeoutHandle, _BaseCoroMixin,
-                      deprecated_noop, proxies_from_env, sentinel,
+from .helpers import (CeilTimeout, TimeoutHandle, proxies_from_env, sentinel,
                       strip_auth_from_url)
 from .http import WS_KEY, WebSocketReader, WebSocketWriter
 from .http_websocket import WSHandshakeError, ws_ext_gen, ws_ext_parse
@@ -145,29 +145,28 @@ def request(self, method, url, **kwargs):
         """Perform HTTP request."""
         return _RequestContextManager(self._request(method, url, **kwargs))
 
-    @asyncio.coroutine
-    def _request(self, method, url, *,
-                 params=None,
-                 data=None,
-                 json=None,
-                 headers=None,
-                 skip_auto_headers=None,
-                 auth=None,
-                 allow_redirects=True,
-                 max_redirects=10,
-                 encoding=None,
-                 compress=None,
-                 chunked=None,
-                 expect100=False,
-                 read_until_eof=True,
-                 proxy=None,
-                 proxy_auth=None,
-                 timeout=sentinel,
-                 verify_ssl=None,
-                 fingerprint=None,
-                 ssl_context=None,
-                 proxy_headers=None,
-                 trace_request_ctx=None):
+    async def _request(self, method, url, *,
+                       params=None,
+                       data=None,
+                       json=None,
+                       headers=None,
+                       skip_auto_headers=None,
+                       auth=None,
+                       allow_redirects=True,
+                       max_redirects=10,
+                       encoding=None,
+                       compress=None,
+                       chunked=None,
+                       expect100=False,
+                       read_until_eof=True,
+                       proxy=None,
+                       proxy_auth=None,
+                       timeout=sentinel,
+                       verify_ssl=None,
+                       fingerprint=None,
+                       ssl_context=None,
+                       proxy_headers=None,
+                       trace_request_ctx=None):
 
         # NOTE: timeout clamps existing connect and read timeouts.  We cannot
         # set the default to None because we need to detect if the user wants
@@ -234,7 +233,7 @@ def _request(self, method, url, *,
         ]
 
         for trace in traces:
-            yield from trace.send_request_start(
+            await trace.send_request_start(
                 method,
                 url,
                 headers
@@ -288,7 +287,7 @@ def _request(self, method, url, *,
                     # connection timeout
                     try:
                         with CeilTimeout(self._conn_timeout, loop=self._loop):
-                            conn = yield from self._connector.connect(
+                            conn = await self._connector.connect(
                                 req,
                                 traces=traces
                             )
@@ -301,7 +300,7 @@ def _request(self, method, url, *,
                     try:
                         resp = req.send(conn)
                         try:
-                            yield from resp.start(conn, read_until_eof)
+                            await resp.start(conn, read_until_eof)
                         except Exception:
                             resp.close()
                             conn.close()
@@ -318,7 +317,7 @@ def _request(self, method, url, *,
                             301, 302, 303, 307, 308) and allow_redirects:
 
                         for trace in traces:
-                            yield from trace.send_request_redirect(
+                            await trace.send_request_redirect(
                                 method,
                                 url,
                                 headers,
@@ -390,7 +389,7 @@ def _request(self, method, url, *,
             resp._history = tuple(history)
 
             for trace in traces:
-                yield from trace.send_request_end(
+                await trace.send_request_end(
                     method,
                     url,
                     headers,
@@ -406,7 +405,7 @@ def _request(self, method, url, *,
                 handle = None
 
             for trace in traces:
-                yield from trace.send_request_exception(
+                await trace.send_request_exception(
                     method,
                     url,
                     headers,
@@ -451,24 +450,23 @@ def ws_connect(self, url, *,
                              proxy_headers=proxy_headers,
                              compress=compress))
 
-    @asyncio.coroutine
-    def _ws_connect(self, url, *,
-                    protocols=(),
-                    timeout=10.0,
-                    receive_timeout=None,
-                    autoclose=True,
-                    autoping=True,
-                    heartbeat=None,
-                    auth=None,
-                    origin=None,
-                    headers=None,
-                    proxy=None,
-                    proxy_auth=None,
-                    verify_ssl=None,
-                    fingerprint=None,
-                    ssl_context=None,
-                    proxy_headers=None,
-                    compress=0):
+    async def _ws_connect(self, url, *,
+                          protocols=(),
+                          timeout=10.0,
+                          receive_timeout=None,
+                          autoclose=True,
+                          autoping=True,
+                          heartbeat=None,
+                          auth=None,
+                          origin=None,
+                          headers=None,
+                          proxy=None,
+                          proxy_auth=None,
+                          verify_ssl=None,
+                          fingerprint=None,
+                          ssl_context=None,
+                          proxy_headers=None,
+                          compress=0):
 
         if headers is None:
             headers = CIMultiDict()
@@ -495,15 +493,15 @@ def _ws_connect(self, url, *,
             headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr
 
         # send request
-        resp = yield from self.get(url, headers=headers,
-                                   read_until_eof=False,
-                                   auth=auth,
-                                   proxy=proxy,
-                                   proxy_auth=proxy_auth,
-                                   verify_ssl=verify_ssl,
-                                   fingerprint=fingerprint,
-                                   ssl_context=ssl_context,
-                                   proxy_headers=proxy_headers)
+        resp = await self.get(url, headers=headers,
+                              read_until_eof=False,
+                              auth=auth,
+                              proxy=proxy,
+                              proxy_auth=proxy_auth,
+                              verify_ssl=verify_ssl,
+                              fingerprint=fingerprint,
+                              ssl_context=ssl_context,
+                              proxy_headers=proxy_headers)
 
         try:
             # check handshake
@@ -663,7 +661,7 @@ def delete(self, url, **kwargs):
             self._request(hdrs.METH_DELETE, url,
                           **kwargs))
 
-    def close(self):
+    async def close(self):
         """Close underlying connector.
 
         Release all acquired resources.
@@ -673,8 +671,6 @@ def close(self):
                 self._connector.close()
             self._connector = None
 
-        return deprecated_noop('ClientSession.close() is a coroutine')
-
     @property
     def closed(self):
         """Is client session closed.
@@ -717,32 +713,45 @@ def __exit__(self, exc_type, exc_val, exc_tb):
         # __exit__ should exist in pair with __enter__ but never executed
         pass  # pragma: no cover
 
-    @asyncio.coroutine
-    def __aenter__(self):
+    async def __aenter__(self):
         return self
 
-    @asyncio.coroutine
-    def __aexit__(self, exc_type, exc_val, exc_tb):
-        yield from self.close()
+    async def __aexit__(self, exc_type, exc_val, exc_tb):
+        await self.close()
 
 
-class _BaseRequestContextManager(_BaseCoroMixin):
+class _BaseRequestContextManager(Coroutine):
 
-    __slots__ = ('_resp',)
+    __slots__ = ('_coro', '_resp')
 
     def __init__(self, coro):
-        super().__init__(coro)
         self._coro = coro
 
-    @asyncio.coroutine
-    def __aenter__(self):
-        self._resp = yield from self._coro
+    async def __aenter__(self):
+        self._resp = await self._coro
         return self._resp
 
+    # @asyncio.coroutine
+    # def __iter__(self):
+    #     ret = yield from self._coro.__await__()
+    #     return ret
+
+    def send(self, arg):
+        return self._coro.send(arg)
+
+    def throw(self, arg):
+        return self._coro.throw(arg)
+
+    def close(self):
+        return self._coro.close()
+
+    def __await__(self):
+        ret = self._coro.__await__()
+        return ret
+
 
 class _RequestContextManager(_BaseRequestContextManager):
-    @asyncio.coroutine
-    def __aexit__(self, exc_type, exc, tb):
+    async def __aexit__(self, exc_type, exc, tb):
         # We're basing behavior on the exception as it can be caused by
         # user code unrelated to the status of the connection.  If you
         # would like to close a connection you must do that
@@ -752,33 +761,26 @@ def __aexit__(self, exc_type, exc, tb):
 
 
 class _WSRequestContextManager(_BaseRequestContextManager):
-    @asyncio.coroutine
-    def __aexit__(self, exc_type, exc, tb):
-        yield from self._resp.close()
+    async def __aexit__(self, exc_type, exc, tb):
+        await self._resp.close()
 
 
-class _SessionRequestContextManager(_RequestContextManager):
+class _SessionRequestContextManager:
 
-    __slots__ = _RequestContextManager.__slots__ + ('_session', )
+    __slots__ = ('_coro', '_resp', '_session')
 
     def __init__(self, coro, session):
-        super().__init__(coro)
+        self._coro = coro
+        self._resp = None
         self._session = session
 
-    @asyncio.coroutine
-    def __iter__(self):
-        try:
-            return (yield from self._coro)
-        except Exception:
-            yield from self._session.close()
-            raise
+    async def __aenter__(self):
+        self._resp = await self._coro
+        return self._resp
 
-    def __await__(self):
-        try:
-            return (yield from self._coro)
-        except Exception:
-            yield from self._session.close()
-            raise
+    async def __aexit__(self, exc_type, exc_val, exc_tb):
+        self._resp.close()
+        await self._session.close()
 
 
 def request(method, url, *,
@@ -859,4 +861,4 @@ def request(method, url, *,
                          read_until_eof=read_until_eof,
                          proxy=proxy,
                          proxy_auth=proxy_auth,),
-        session=session)
+        session)
diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py
--- a/aiohttp/helpers.py
+++ b/aiohttp/helpers.py
@@ -10,10 +10,8 @@
 import os
 import re
 import time
-import warnings
 import weakref
 from collections import namedtuple
-from collections.abc import Coroutine
 from contextlib import suppress
 from math import ceil
 from pathlib import Path
@@ -41,79 +39,6 @@
 TOKEN = CHAR ^ CTL ^ SEPARATORS
 
 
-class _BaseCoroMixin(Coroutine):
-
-    __slots__ = ('_coro',)
-
-    def __init__(self, coro):
-        self._coro = coro
-
-    def send(self, arg):
-        return self._coro.send(arg)
-
-    def throw(self, arg):
-        return self._coro.throw(arg)
-
-    def close(self):
-        return self._coro.close()
-
-    @property
-    def gi_frame(self):
-        return self._coro.gi_frame
-
-    @property
-    def gi_running(self):
-        return self._coro.gi_running
-
-    @property
-    def gi_code(self):
-        return self._coro.gi_code
-
-    def __next__(self):
-        return self.send(None)
-
-    @asyncio.coroutine
-    def __iter__(self):
-        ret = yield from self._coro
-        return ret
-
-    def __await__(self):
-        ret = yield from self._coro
-        return ret
-
-
-class _CoroGuard(_BaseCoroMixin):
-    """Only to be used with func:`deprecated_noop`.
-
-    Otherwise the stack information in the raised warning doesn't line up with
-    the user's code anymore.
-    """
-    __slots__ = ('_msg', '_awaited')
-
-    def __init__(self, coro, msg):
-        super().__init__(coro)
-        self._msg = msg
-        self._awaited = False
-
-    def send(self, arg):
-        self._awaited = True
-        return self._coro.send(arg)
-
-    @asyncio.coroutine
-    def __iter__(self):
-        self._awaited = True
-        return super().__iter__()
-
-    def __await__(self):
-        self._awaited = True
-        return super().__await__()
-
-    def __del__(self):
-        self._coro = None
-        if not self._awaited:
-            warnings.warn(self._msg, DeprecationWarning, stacklevel=2)
-
-
 coroutines = asyncio.coroutines
 old_debug = coroutines._DEBUG
 coroutines._DEBUG = False
@@ -124,10 +49,6 @@ def noop(*args, **kwargs):
     return
 
 
-def deprecated_noop(message):
-    return _CoroGuard(noop(), message)
-
-
 coroutines._DEBUG = old_debug
 
 


Test patch:
diff --git a/aiohttp/test_utils.py b/aiohttp/test_utils.py
--- a/aiohttp/test_utils.py
+++ b/aiohttp/test_utils.py
@@ -220,8 +220,7 @@ def session(self):
     def make_url(self, path):
         return self._server.make_url(path)
 
-    @asyncio.coroutine
-    def request(self, method, path, *args, **kwargs):
+    async def request(self, method, path, *args, **kwargs):
         """Routes a request to tested http server.
 
         The interface is identical to asyncio.ClientSession.request,
@@ -229,7 +228,7 @@ def request(self, method, path, *args, **kwargs):
         test server.
 
         """
-        resp = yield from self._session.request(
+        resp = await self._session.request(
             method, self.make_url(path), *args, **kwargs
         )
         # save it to close later
@@ -288,9 +287,8 @@ def ws_connect(self, path, *args, **kwargs):
             self._ws_connect(path, *args, **kwargs)
         )
 
-    @asyncio.coroutine
-    def _ws_connect(self, path, *args, **kwargs):
-        ws = yield from self._session.ws_connect(
+    async def _ws_connect(self, path, *args, **kwargs):
+        ws = await self._session.ws_connect(
             self.make_url(path), *args, **kwargs)
         self._websockets.append(ws)
         return ws
diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py
--- a/tests/test_client_functional.py
+++ b/tests/test_client_functional.py
@@ -350,7 +350,7 @@ async def handler(request):
     assert "{}".format(task).startswith("<Task pending")
     resp = await task
     resp.close()
-    client.close()
+    await client.close()
 
 
 async def test_str_params(loop, test_client):
@@ -1936,7 +1936,7 @@ async def test_request_conn_error(loop):
     client = aiohttp.ClientSession(loop=loop)
     with pytest.raises(aiohttp.ClientConnectionError):
         await client.get('http://0.0.0.0:1')
-    client.close()
+    await client.close()
 
 
 @pytest.mark.xfail
@@ -2232,7 +2232,7 @@ async def close(self):
     assert session.closed
 
 
-async def test_aiohttp_request(loop, test_server):
+async def test_aiohttp_request_context_manager(loop, test_server):
     async def handler(request):
         return web.Response()
 
@@ -2244,7 +2244,22 @@ async def handler(request):
         await resp.read()
         assert resp.status == 200
 
-    resp = await aiohttp.request('GET', server.make_url('/'), loop=loop)
-    await resp.read()
-    assert resp.status == 200
-    assert resp.connection is None
+
+async def test_aiohttp_request_ctx_manager_not_found(loop):
+
+    with pytest.raises(aiohttp.ClientConnectionError):
+        async with aiohttp.request('GET', 'http://wrong-dns-name.com',
+                                   loop=loop):
+            assert False, "never executed"  # pragma: no cover
+
+
+async def test_aiohttp_request_coroutine(loop, test_server):
+    async def handler(request):
+        return web.Response()
+
+    app = web.Application()
+    app.router.add_get('/', handler)
+    server = await test_server(app)
+
+    with pytest.raises(TypeError):
+        await aiohttp.request('GET', server.make_url('/'), loop=loop)
diff --git a/tests/test_client_functional_oldstyle.py b/tests/test_client_functional_oldstyle.py
--- a/tests/test_client_functional_oldstyle.py
+++ b/tests/test_client_functional_oldstyle.py
@@ -373,7 +373,7 @@ def test_POST_DATA_with_charset(self):
             self.assertEqual('текст', field['data'])
             self.assertEqual(r.status, 200)
             r.close()
-            session.close()
+            self.loop.run_until_complete(session.close())
 
     def test_POST_DATA_with_charset_pub_request(self):
         with run_server(self.loop, router=Functional) as httpd:
@@ -383,8 +383,9 @@ def test_POST_DATA_with_charset_pub_request(self):
             form.add_field('name', 'текст',
                            content_type='text/plain; charset=koi8-r')
 
+            session = client.ClientSession(loop=self.loop)
             r = self.loop.run_until_complete(
-                aiohttp.request('post', url, data=form, loop=self.loop))
+                session.request('post', url, data=form))
             content = self.loop.run_until_complete(r.json())
 
             self.assertEqual(1, len(content['multipart-data']))
@@ -393,6 +394,7 @@ def test_POST_DATA_with_charset_pub_request(self):
             self.assertEqual('текст', field['data'])
             self.assertEqual(r.status, 200)
             r.close()
+            self.loop.run_until_complete(session.close())
 
     def test_POST_DATA_with_content_transfer_encoding(self):
         with run_server(self.loop, router=Functional) as httpd:
@@ -415,7 +417,7 @@ def test_POST_DATA_with_content_transfer_encoding(self):
             self.assertEqual(r.status, 200)
 
             r.close()
-            session.close()
+            self.loop.run_until_complete(session.close())
 
     def test_POST_MULTIPART(self):
         with run_server(self.loop, router=Functional) as httpd:
@@ -445,7 +447,7 @@ def test_POST_MULTIPART(self):
                 content['multipart-data'][2])
             self.assertEqual(r.status, 200)
             r.close()
-            session.close()
+            self.loop.run_until_complete(session.close())
 
     def test_POST_STREAM_DATA(self):
         with run_server(self.loop, router=Functional) as httpd:
@@ -473,7 +475,7 @@ async def stream(writer):
                     headers={'Content-Length': str(len(data))}))
             content = self.loop.run_until_complete(r.json())
             r.close()
-            session.close()
+            self.loop.run_until_complete(session.close())
 
             self.assertEqual(str(len(data)),
                              content['headers']['Content-Length'])
@@ -501,7 +503,7 @@ def test_POST_StreamReader(self):
                     headers={'Content-Length': str(len(data))}))
             content = self.loop.run_until_complete(r.json())
             r.close()
-            session.close()
+            self.loop.run_until_complete(session.close())
 
             self.assertEqual(str(len(data)),
                              content['headers']['Content-Length'])
@@ -528,7 +530,7 @@ def test_POST_DataQueue(self):
                     headers={'Content-Length': str(len(data))}))
             content = self.loop.run_until_complete(r.json())
             r.close()
-            session.close()
+            self.loop.run_until_complete(session.close())
 
             self.assertEqual(str(len(data)),
                              content['headers']['Content-Length'])
@@ -557,7 +559,7 @@ def test_POST_ChunksQueue(self):
                     headers={'Content-Length': str(len(data))}))
             content = self.loop.run_until_complete(r.json())
             r.close()
-            session.close()
+            self.loop.run_until_complete(session.close())
 
             self.assertEqual(str(len(data)),
                              content['headers']['Content-Length'])
@@ -570,7 +572,7 @@ def test_request_conn_closed(self):
                 self.loop.run_until_complete(
                     session.request('get', httpd.url('method', 'get')))
 
-            session.close()
+            self.loop.run_until_complete(session.close())
 
     def test_session_close(self):
         conn = aiohttp.TCPConnector(loop=self.loop)
@@ -592,7 +594,7 @@ def test_session_close(self):
             self.assertEqual(content['content'], 'requests=1')
             r.close()
 
-        session.close()
+        self.loop.run_until_complete(session.close())
         conn.close()
 
     def test_multidict_headers(self):
@@ -613,7 +615,7 @@ def test_multidict_headers(self):
             self.assertEqual(str(len(data)),
                              content['headers']['Content-Length'])
 
-        session.close()
+        self.loop.run_until_complete(session.close())
 
     def test_dont_close_explicit_connector(self):
 
@@ -625,7 +627,7 @@ async def go(url):
             await r.read()
             self.assertEqual(1, len(connector._conns))
             connector.close()
-            session.close()
+            await session.close()
 
         with run_server(self.loop, router=Functional) as httpd:
             url = httpd.url('keepalive')
@@ -667,7 +669,7 @@ async def go():
                 r = await session.request('GET', url)
                 await r.read()
                 self.assertEqual(0, len(connector._conns))
-            session.close()
+            await session.close()
             connector.close()
             server.close()
             await server.wait_closed()
@@ -714,7 +716,7 @@ async def go():
                 await session.request('GET', url)
             self.assertEqual(0, len(connector._conns))
 
-            session.close()
+            await session.close()
             connector.close()
             server.close()
             await server.wait_closed()
@@ -744,7 +746,7 @@ def test_session_cookies(self, m_log):
             self.assertEqual(
                 content['headers']['Cookie'], 'c1=cookie1; c2=cookie2')
             r.close()
-            session.close()
+            self.loop.run_until_complete(session.close())
 
     def test_session_headers(self):
         with run_server(self.loop, router=Functional) as httpd:
@@ -762,7 +764,7 @@ def test_session_headers(self):
             self.assertEqual(
                 content['headers']["X-Real-Ip"], "192.168.0.1")
             r.close()
-            session.close()
+            self.loop.run_until_complete(session.close())
 
     def test_session_headers_merge(self):
         with run_server(self.loop, router=Functional) as httpd:
@@ -785,7 +787,7 @@ def test_session_headers_merge(self):
             self.assertEqual(
                 content['headers']["X-Sent-By"], "aiohttp")
             r.close()
-            session.close()
+            self.loop.run_until_complete(session.close())
 
     def test_session_auth(self):
         with run_server(self.loop, router=Functional) as httpd:
@@ -801,7 +803,7 @@ def test_session_auth(self):
             self.assertEqual(
                 content['headers']["Authorization"], "Basic bG9naW46cGFzcw==")
             r.close()
-            session.close()
+            self.loop.run_until_complete(session.close())
 
     def test_session_auth_override(self):
         with run_server(self.loop, router=Functional) as httpd:
@@ -819,7 +821,7 @@ def test_session_auth_override(self):
                 content['headers']["Authorization"],
                 "Basic b3RoZXJfbG9naW46cGFzcw==")
             r.close()
-            session.close()
+            self.loop.run_until_complete(session.close())
 
     def test_session_auth_header_conflict(self):
         with run_server(self.loop, router=Functional) as httpd:
@@ -831,4 +833,4 @@ def test_session_auth_header_conflict(self):
                 self.loop.run_until_complete(
                     session.request('get', httpd.url('method', 'get'),
                                     headers=headers))
-            session.close()
+            self.loop.run_until_complete(session.close())
diff --git a/tests/test_client_request.py b/tests/test_client_request.py
--- a/tests/test_client_request.py
+++ b/tests/test_client_request.py
@@ -1091,7 +1091,7 @@ async def create_connection(req, traces=None):
     assert isinstance(resp, CustomResponse)
     assert called
     resp.close()
-    session.close()
+    await session.close()
     conn.close()
 
 
diff --git a/tests/test_client_session.py b/tests/test_client_session.py
--- a/tests/test_client_session.py
+++ b/tests/test_client_session.py
@@ -34,7 +34,7 @@ def maker(*args, **kwargs):
         return session
     yield maker
     if session is not None:
-        session.close()
+        loop.run_until_complete(session.close())
 
 
 @pytest.fixture
@@ -60,16 +60,6 @@ def test_close_coro(create_session, loop):
     loop.run_until_complete(session.close())
 
 
-async def test_close_deprecated(create_session):
-    session = create_session()
-
-    with pytest.warns(DeprecationWarning) as ctx:
-        session.close()
-
-    # Assert the warning points at us and not at _CoroGuard.
-    assert ctx.list[0].filename == __file__
-
-
 def test_init_headers_simple_dict(create_session):
     session = create_session(headers={"h1": "header1",
                                       "h2": "header2"})
@@ -259,37 +249,37 @@ def test_http_DELETE(session, params):
                                            **params)]
 
 
-def test_close(create_session, connector):
+async def test_close(create_session, connector):
     session = create_session(connector=connector)
 
-    session.close()
+    await session.close()
     assert session.connector is None
     assert connector.closed
 
 
-def test_closed(session):
+async def test_closed(session):
     assert not session.closed
-    session.close()
+    await session.close()
     assert session.closed
 
 
-def test_connector(create_session, loop, mocker):
+async def test_connector(create_session, loop, mocker):
     connector = TCPConnector(loop=loop)
     mocker.spy(connector, 'close')
     session = create_session(connector=connector)
     assert session.connector is connector
 
-    session.close()
+    await session.close()
     assert connector.close.called
     connector.close()
 
 
-def test_create_connector(create_session, loop, mocker):
+async def test_create_connector(create_session, loop, mocker):
     session = create_session()
     connector = session.connector
     mocker.spy(session.connector, 'close')
 
-    session.close()
+    await session.close()
     assert connector.close.called
 
 
@@ -318,7 +308,7 @@ def test_detach(session):
 
 
 async def test_request_closed_session(session):
-    session.close()
+    await session.close()
     with pytest.raises(RuntimeError):
         await session.request('get', '/')
 
@@ -330,12 +320,12 @@ def test_close_flag_for_closed_connector(session):
     assert session.closed
 
 
-def test_double_close(connector, create_session):
+async def test_double_close(connector, create_session):
     session = create_session(connector=connector)
 
-    session.close()
+    await session.close()
     assert session.connector is None
-    session.close()
+    await session.close()
     assert session.closed
     assert connector.closed
 
@@ -358,12 +348,12 @@ def test_context_manager(connector, loop):
         assert session.closed
 
 
-def test_borrow_connector_loop(connector, create_session, loop):
+async def test_borrow_connector_loop(connector, create_session, loop):
     session = ClientSession(connector=connector, loop=None)
     try:
         assert session._loop, loop
     finally:
-        session.close()
+        await session.close()
 
 
 async def test_reraise_os_error(create_session):
@@ -431,10 +421,10 @@ def test_session_default_version(loop):
     assert session.version == aiohttp.HttpVersion11
 
 
-def test_session_loop(loop):
+async def test_session_loop(loop):
     session = aiohttp.ClientSession(loop=loop)
     assert session.loop is loop
-    session.close()
+    await session.close()
 
 
 def test_proxy_str(session, params):
@@ -457,7 +447,7 @@ def test_client_session_implicit_loop_warn():
     with pytest.warns(ResourceWarning):
         session = aiohttp.ClientSession()
         assert session._loop is loop
-        session.close()
+        loop.run_until_complete(session.close())
 
     asyncio.set_event_loop(None)
     loop.close()
diff --git a/tests/test_client_ws.py b/tests/test_client_ws.py
--- a/tests/test_client_ws.py
+++ b/tests/test_client_ws.py
@@ -262,7 +262,7 @@ async def test_close(loop, ws_key, key_data):
                 assert not res
                 assert writer.close.call_count == 1
 
-                session.close()
+                await session.close()
 
 
 async def test_close_exc(loop, ws_key, key_data):
@@ -292,7 +292,7 @@ async def test_close_exc(loop, ws_key, key_data):
                 assert resp.closed
                 assert resp.exception() is exc
 
-                session.close()
+                await session.close()
 
 
 async def test_close_exc2(loop, ws_key, key_data):
@@ -411,7 +411,7 @@ async def test_reader_read_exception(ws_key, key_data, loop):
                 assert msg.type == aiohttp.WSMsgType.ERROR
                 assert resp.exception() is exc
 
-                session.close()
+                await session.close()
 
 
 async def test_receive_runtime_err(loop):
diff --git a/tests/test_connector.py b/tests/test_connector.py
--- a/tests/test_connector.py
+++ b/tests/test_connector.py
@@ -1818,7 +1818,7 @@ async def handler(request):
         self.assertIsInstance(ctx.value.os_error, ssl.SSLError)
         self.assertTrue(ctx.value, aiohttp.ClientSSLError)
 
-        session.close()
+        self.loop.run_until_complete(session.close())
         conn.close()
 
     def test_tcp_connector_do_not_raise_connector_ssl_error(self):
@@ -1855,7 +1855,7 @@ async def handler(request):
         self.assertIs(_sslcontext, sslcontext)
         r.close()
 
-        session.close()
+        self.loop.run_until_complete(session.close())
         conn.close()
 
     def test_tcp_connector_uses_provided_local_addr(self):
@@ -1881,7 +1881,7 @@ async def handler(request):
         self.assertEqual(
             first_conn.transport._sock.getsockname(), ('127.0.0.1', port))
         r.close()
-        session.close()
+        self.loop.run_until_complete(session.close())
         conn.close()
 
     @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'requires unix')
@@ -1901,7 +1901,7 @@ async def handler(request):
             session.request('get', url))
         self.assertEqual(r.status, 200)
         r.close()
-        session.close()
+        self.loop.run_until_complete(session.close())
 
     def test_resolver_not_called_with_address_is_ip(self):
         resolver = mock.MagicMock()
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -12,31 +12,6 @@
 from aiohttp.abc import AbstractAccessLogger
 
 
-# -------------------- coro guard --------------------------------
-
-async def test_warn():
-    with pytest.warns(DeprecationWarning) as ctx:
-        helpers.deprecated_noop('Text')
-
-    w = ctx.list[0]
-
-    assert str(w.message) == 'Text'
-    # Assert the warning points at us and not at _CoroGuard.
-    assert w.filename == __file__
-
-
-async def test_no_warn_on_await():
-    with pytest.warns(None) as ctx:
-        await helpers.deprecated_noop('Text')
-    assert not ctx.list
-
-
-def test_coro_guard_close():
-    guard = helpers.deprecated_noop('Text')
-    guard.close()
-    assert not guard.gi_running
-
-
 # ------------------- parse_mimetype ----------------------------------
 
 @pytest.mark.parametrize('mimetype, expected', [
diff --git a/tests/test_proxy_functional.py b/tests/test_proxy_functional.py
--- a/tests/test_proxy_functional.py
+++ b/tests/test_proxy_functional.py
@@ -198,7 +198,7 @@ async def test_proxy_http_acquired_cleanup(proxy_test_server, loop):
 
     assert 0 == len(conn._acquired)
 
-    sess.close()
+    await sess.close()
 
 
 @pytest.mark.skip('we need to reconsider how we test this')
diff --git a/tests/test_test_utils.py b/tests/test_test_utils.py
--- a/tests/test_test_utils.py
+++ b/tests/test_test_utils.py
@@ -223,7 +223,7 @@ async def test_test_server_context_manager(loop):
         resp = await client.head(server.make_url('/'))
         assert resp.status == 200
         resp.close()
-        client.close()
+        await client.close()
 
 
 def test_client_unsupported_arg():