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 py_35 constant



Discussion:

# [Codecov](https://codecov.io/gh/aio-libs/aiohttp/pull/2363?src=pr&el=h1) Report
> Merging [#2363](https://codecov.io/gh/aio-libs/aiohttp/pull/2363?src=pr&el=desc) into [master](https://codecov.io/gh/aio-libs/aiohttp/commit/9102c350361acddca93545bc3295087910cd96ed?src=pr&el=desc) will **increase** coverage by `0.21%`.
> The diff coverage is `93.05%`.

[![Impacted file tree graph](https://codecov.io/gh/aio-libs/aiohttp/pull/2363/graphs/tree.svg?width=650&height=150&src=pr&token=LJn22G3rd7)](https://codecov.io/gh/aio-libs/aiohttp/pull/2363?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #2363      +/-   ##
==========================================
+ Coverage   96.96%   97.17%   +0.21%     
==========================================
  Files          39       39              
  Lines        8199     8180      -19     
  Branches     1437     1419      -18     
==========================================
- Hits         7950     7949       -1     
  Misses        101      101              
+ Partials      148      130      -18
```


| [Impacted Files](https://codecov.io/gh/aio-libs/aiohttp/pull/2363?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [aiohttp/web\_urldispatcher.py](https://codecov.io/gh/aio-libs/aiohttp/pull/2363?src=pr&el=tree#diff-YWlvaHR0cC93ZWJfdXJsZGlzcGF0Y2hlci5weQ==) | `99.33% <100%> (+0.16%)` | :arrow_up: |
| [aiohttp/client\_reqrep.py](https://codecov.io/gh/aio-libs/aiohttp/pull/2363?src=pr&el=tree#diff-YWlvaHR0cC9jbGllbnRfcmVxcmVwLnB5) | `97.35% <100%> (+0.19%)` | :arrow_up: |
| [aiohttp/test\_utils.py](https://codecov.io/gh/aio-libs/aiohttp/pull/2363?src=pr&el=tree#diff-YWlvaHR0cC90ZXN0X3V0aWxzLnB5) | `98.58% <100%> (+0.69%)` | :arrow_up: |
| [aiohttp/streams.py](https://codecov.io/gh/aio-libs/aiohttp/pull/2363?src=pr&el=tree#diff-YWlvaHR0cC9zdHJlYW1zLnB5) | `99.06% <100%> (+0.69%)` | :arrow_up: |
| [aiohttp/helpers.py](https://codecov.io/gh/aio-libs/aiohttp/pull/2363?src=pr&el=tree#diff-YWlvaHR0cC9oZWxwZXJzLnB5) | `97.16% <100%> (+0.22%)` | :arrow_up: |
| [aiohttp/multipart.py](https://codecov.io/gh/aio-libs/aiohttp/pull/2363?src=pr&el=tree#diff-YWlvaHR0cC9tdWx0aXBhcnQucHk=) | `95.42% <77.27%> (+0.52%)` | :arrow_up: |
| [examples/web\_ws.py](https://codecov.io/gh/aio-libs/aiohttp/pull/2363?src=pr&el=tree#diff-ZXhhbXBsZXMvd2ViX3dzLnB5) | `94.84% <0%> (+0.37%)` | :arrow_up: |
| [examples/client\_ws.py](https://codecov.io/gh/aio-libs/aiohttp/pull/2363?src=pr&el=tree#diff-ZXhhbXBsZXMvY2xpZW50X3dzLnB5) | `96.42% <0%> (+0.48%)` | :arrow_up: |
| [tests/autobahn/client.py](https://codecov.io/gh/aio-libs/aiohttp/pull/2363?src=pr&el=tree#diff-dGVzdHMvYXV0b2JhaG4vY2xpZW50LnB5) | `94.92% <0%> (+1.31%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/aio-libs/aiohttp/pull/2363?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/aio-libs/aiohttp/pull/2363?src=pr&el=footer). Last update [9102c35...982ab0a](https://codecov.io/gh/aio-libs/aiohttp/pull/2363?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Resolving patch:
diff --git a/aiohttp/client.py b/aiohttp/client.py
--- a/aiohttp/client.py
+++ b/aiohttp/client.py
@@ -711,11 +711,11 @@ def detach(self):
         self._connector = None
 
     def __enter__(self):
-        warnings.warn("Use async with instead", DeprecationWarning)
-        return self
+        raise TypeError("Use async with instead")
 
     def __exit__(self, exc_type, exc_val, exc_tb):
-        self.close()
+        # __exit__ should exist in pair with __enter__ but never executed
+        pass  # pragma: no cover
 
     @asyncio.coroutine
     def __aenter__(self):


Test patch:
diff --git a/aiohttp/test_utils.py b/aiohttp/test_utils.py
--- a/aiohttp/test_utils.py
+++ b/aiohttp/test_utils.py
@@ -118,11 +118,11 @@ async def _close_hook(self):
         pass  # pragma: no cover
 
     def __enter__(self):
-        self._loop.run_until_complete(self.start_server(loop=self._loop))
-        return self
+        raise TypeError("Use async with instead")
 
     def __exit__(self, exc_type, exc_value, traceback):
-        self._loop.run_until_complete(self.close())
+        # __exit__ should exist in pair with __enter__ but never executed
+        pass  # pragma: no cover
 
     async def __aenter__(self):
         await self.start_server(loop=self._loop)
@@ -317,11 +317,11 @@ async def close(self):
             self._closed = True
 
     def __enter__(self):
-        self._loop.run_until_complete(self.start_server())
-        return self
+        raise TypeError("Use async with instead")
 
     def __exit__(self, exc_type, exc_value, traceback):
-        self._loop.run_until_complete(self.close())
+        # __exit__ should exist in pair with __enter__ but never executed
+        pass  # pragma: no cover
 
     async def __aenter__(self):
         await self.start_server()
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
@@ -2,7 +2,6 @@
 import contextlib
 import gc
 import re
-import types
 from http.cookies import SimpleCookie
 from unittest import mock
 
@@ -352,11 +351,11 @@ def test_del(connector, loop):
 
 
 def test_context_manager(connector, loop):
-    with pytest.warns(DeprecationWarning):
+    with pytest.raises(TypeError):
         with ClientSession(loop=loop, connector=connector) as session:
             pass
 
-    assert session.closed
+        assert session.closed
 
 
 def test_borrow_connector_loop(connector, create_session, loop):
@@ -386,19 +385,6 @@ async def create_connection(req, traces=None):
     assert e.strerror == err.strerror
 
 
-async def test_request_ctx_manager_props(loop):
-    await asyncio.sleep(0, loop=loop)  # to make it a task
-    with pytest.warns(DeprecationWarning):
-        with aiohttp.ClientSession(loop=loop) as client:
-            ctx_mgr = client.get('http://example.com')
-
-            next(ctx_mgr)
-            assert isinstance(ctx_mgr.gi_frame, types.FrameType)
-            assert not ctx_mgr.gi_running
-            assert isinstance(ctx_mgr.gi_code, types.CodeType)
-            await asyncio.sleep(0.1, loop=loop)
-
-
 async def test_cookie_jar_usage(loop, test_client):
     req_url = None
 
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
@@ -23,10 +23,6 @@ def _create_example_app():
     async def hello(request):
         return web.Response(body=_hello_world_bytes)
 
-    async def gzip_hello(request):
-        return web.Response(body=_hello_world_gz,
-                            headers={'Content-Encoding': 'gzip'})
-
     async def websocket_handler(request):
 
         ws = web.WebSocketResponse()
@@ -47,7 +43,6 @@ async def cookie_handler(request):
 
     app = web.Application()
     app.router.add_route('*', '/', hello)
-    app.router.add_route('*', '/gzip_hello', gzip_hello)
     app.router.add_route('*', '/websocket', websocket_handler)
     app.router.add_route('*', '/cookie', cookie_handler)
     return app
@@ -73,64 +68,18 @@ def test_client(loop, app):
     loop.run_until_complete(client.close())
 
 
-def test_full_server_scenario():
-    with loop_context() as loop:
-        app = _create_example_app()
-        with _TestClient(_TestServer(app, loop=loop), loop=loop) as client:
-
-            async def test_get_route():
-                nonlocal client
-                resp = await client.request("GET", "/")
-                assert resp.status == 200
-                text = await resp.text()
-                assert _hello_world_str == text
-
-            loop.run_until_complete(test_get_route())
-
-
-def test_auto_gzip_decompress():
-    with loop_context() as loop:
-        app = _create_example_app()
-        with _TestClient(_TestServer(app, loop=loop), loop=loop) as client:
-
-            async def test_get_route():
-                nonlocal client
-                resp = await client.request("GET", "/gzip_hello")
-                assert resp.status == 200
-                data = await resp.read()
-                assert data == _hello_world_bytes
-
-            loop.run_until_complete(test_get_route())
-
-
-def test_noauto_gzip_decompress():
-    with loop_context() as loop:
-        app = _create_example_app()
-        with _TestClient(_TestServer(app, loop=loop), loop=loop,
-                         auto_decompress=False) as client:
-
-            async def test_get_route():
-                nonlocal client
-                resp = await client.request("GET", "/gzip_hello")
-                assert resp.status == 200
-                data = await resp.read()
-                assert data == _hello_world_gz
-
-            loop.run_until_complete(test_get_route())
-
-
-def test_server_with_create_test_teardown():
-    with loop_context() as loop:
-        app = _create_example_app()
-        with _TestClient(_TestServer(app, loop=loop), loop=loop) as client:
+def test_with_test_server_fails(loop):
+    app = _create_example_app()
+    with pytest.raises(TypeError):
+        with _TestServer(app, loop=loop):
+            pass
 
-            async def test_get_route():
-                resp = await client.request("GET", "/")
-                assert resp.status == 200
-                text = await resp.text()
-                assert _hello_world_str == text
 
-            loop.run_until_complete(test_get_route())
+def test_with_test_client_fails(loop):
+    app = _create_example_app()
+    with pytest.raises(TypeError):
+        with _TestClient(_TestServer(app, loop=loop), loop=loop):
+            pass
 
 
 def test_test_client_close_is_idempotent():
@@ -255,29 +204,26 @@ def test_make_mocked_request_transport():
     assert req.transport is transport
 
 
-def test_test_client_props(loop):
+async def test_test_client_props(loop):
     app = _create_example_app()
     client = _TestClient(_TestServer(app, host='localhost', loop=loop),
                          loop=loop)
     assert client.host == 'localhost'
     assert client.port is None
-    with client:
+    async with client:
         assert isinstance(client.port, int)
         assert client.server is not None
     assert client.port is None
 
 
-def test_test_server_context_manager(loop):
+async def test_test_server_context_manager(loop):
     app = _create_example_app()
-    with _TestServer(app, loop=loop) as server:
-        async def go():
-            client = aiohttp.ClientSession(loop=loop)
-            resp = await client.head(server.make_url('/'))
-            assert resp.status == 200
-            resp.close()
-            client.close()
-
-        loop.run_until_complete(go())
+    async with _TestServer(app, loop=loop) as server:
+        client = aiohttp.ClientSession(loop=loop)
+        resp = await client.head(server.make_url('/'))
+        assert resp.status == 200
+        resp.close()
+        client.close()
 
 
 def test_client_unsupported_arg():
@@ -285,9 +231,9 @@ def test_client_unsupported_arg():
         _TestClient('string')
 
 
-def test_server_make_url_yarl_compatibility(loop):
+async def test_server_make_url_yarl_compatibility(loop):
     app = _create_example_app()
-    with _TestServer(app, loop=loop) as server:
+    async with _TestServer(app, loop=loop) as server:
         make_url = server.make_url
         assert make_url(URL('/foo')) == make_url('/foo')
         with pytest.raises(AssertionError):