Repository: aio-libs/aiohttp

Issue:
web.Request and web.Response are no longer weak referenceable
## Long story short

https://github.com/aio-libs/aiohttp/pull/3942 added `__slots__` to request and response classes. This made it impossible to create weak references to them.
In some cases this might be useful. Example: https://github.com/getsentry/sentry-python/issues/560

Possible solution is adding `__weakref__` to `__slots__` in `web.BaseRequest` and `web.StreamResponse`.

## Expected behaviour

web.Request and web.Response to be valid targets for weak references.

## Actual behaviour

web.Request and web.Response are no longer valid targets for weak references.

## Steps to reproduce

```python
import weakref

wekref.ref(request)
# TypeError: cannot create weak reference to 'Request' object
```



Discussion:
Duplicate of #4368.

Resolving patch:
diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py
--- a/aiohttp/web_request.py
+++ b/aiohttp/web_request.py
@@ -113,7 +113,7 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
         '_method', '_version', '_rel_url', '_post', '_read_bytes',
         '_state', '_cache', '_task', '_client_max_size', '_loop',
         '_transport_sslcontext', '_transport_peername',
-        '_disconnection_waiters',
+        '_disconnection_waiters', '__weakref__'
     )
 
     def __init__(self, message: RawRequestMessage,
diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py
--- a/aiohttp/web_response.py
+++ b/aiohttp/web_response.py
@@ -68,7 +68,7 @@ class StreamResponse(BaseClass, HeadersMixin):
     __slots__ = ('_length_check', '_body', '_keep_alive', '_chunked',
                  '_compression', '_compression_force', '_cookies', '_req',
                  '_payload_writer', '_eof_sent', '_body_length', '_state',
-                 '_headers', '_status', '_reason')
+                 '_headers', '_status', '_reason', '__weakref__')
 
     def __init__(self, *,
                  status: int=200,


Test patch:
diff --git a/tests/test_web_request.py b/tests/test_web_request.py
--- a/tests/test_web_request.py
+++ b/tests/test_web_request.py
@@ -1,5 +1,6 @@
 import asyncio
 import socket
+import weakref
 from collections.abc import MutableMapping
 from typing import Any
 from unittest import mock
@@ -765,3 +766,8 @@ async def handler(request):
         resp_text = await resp.text()
         assert resp_text == ('Attempt to decode JSON with '
                              'unexpected mimetype: text/plain')
+
+
+def test_weakref_creation() -> None:
+    req = make_mocked_request('GET', '/')
+    weakref.ref(req)
diff --git a/tests/test_web_response.py b/tests/test_web_response.py
--- a/tests/test_web_response.py
+++ b/tests/test_web_response.py
@@ -3,6 +3,7 @@
 import gzip
 import json
 import re
+import weakref
 from concurrent.futures import ThreadPoolExecutor
 from unittest import mock
 
@@ -1091,6 +1092,11 @@ def test_response_with_immutable_headers() -> None:
                             'Content-Type': 'text/plain; charset=utf-8'}
 
 
+def test_weakref_creation() -> None:
+    resp = Response()
+    weakref.ref(resp)
+
+
 class TestJSONResponse:
 
     def test_content_type_is_application_json_by_default(self) -> None: