diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f0b1621013..8599563b60b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,9 +51,15 @@ jobs: python -m pip install . env: AIOHTTP_NO_EXTENSIONS: 1 - - name: Run linters + - name: Run mypy run: | make mypy + - name: Run slotscheck + run: | + # Some extra requirements are needed to ensure all modules + # can be scanned by slotscheck. + pip install -r requirements/base.txt -c requirements/constraints.txt + slotscheck -v -m aiohttp - name: Install libenchant-dev run: | sudo apt install libenchant-dev diff --git a/CHANGES/6547.bugfix b/CHANGES/6547.bugfix new file mode 100644 index 00000000000..8512c4067f6 --- /dev/null +++ b/CHANGES/6547.bugfix @@ -0,0 +1,2 @@ +Remove overlapping slots in ``RequestHandler``, +fix broken slots inheritance in :py:class:`~aiohttp.web.StreamResponse`. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index d8be9a57a0f..a9f82da88ef 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -46,6 +46,7 @@ Anes Abismail Antoine Pietri Anton Kasyanov Anton Zhdan-Pushkin +Arie Bovenberg Arseny Timoniq Artem Yushkovskiy Arthur Darcet diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py index b7a033795fb..febc49647c5 100644 --- a/aiohttp/helpers.py +++ b/aiohttp/helpers.py @@ -831,9 +831,17 @@ def __repr__(self) -> str: class CookieMixin: + # The `_cookies` slots is not defined here because non-empty slots cannot + # be combined with an Exception base class, as is done in HTTPException. + # CookieMixin subclasses with slots should define the `_cookies` + # slot themselves. + __slots__ = () + def __init__(self) -> None: super().__init__() - self._cookies: SimpleCookie[str] = SimpleCookie() + # Mypy doesn't like that _cookies isn't in __slots__. + # See the comment on this class's __slots__ for why this is OK. + self._cookies: SimpleCookie[str] = SimpleCookie() # type: ignore[misc] @property def cookies(self) -> "SimpleCookie[str]": diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index 03d0c274719..6d662949d8f 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -170,7 +170,6 @@ class RequestHandler(BaseProtocol): "_upgrade", "_payload_parser", "_request_parser", - "_reading_paused", "logger", "access_log", "access_logger", diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index 42c3fac51ae..6ac280ebefa 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -92,6 +92,7 @@ class StreamResponse(BaseClass, HeadersMixin, CookieMixin): "_headers", "_status", "_reason", + "_cookies", "__weakref__", ) diff --git a/requirements/lint.txt b/requirements/lint.txt index c4949763d70..0324cc84401 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -2,3 +2,4 @@ mypy==0.931; implementation_name=="cpython" pre-commit==2.17.0 pytest==6.2.5 +slotscheck==0.8.0 diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 9cfebc7b3a6..26afe82ef7e 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -850,8 +850,14 @@ def test_is_expected_content_type_non_json_not_match(): ) +# It's necessary to subclass CookieMixin before using it. +# See the comments on its __slots__. +class CookieImplementation(helpers.CookieMixin): + pass + + def test_cookies_mixin(): - sut = helpers.CookieMixin() + sut = CookieImplementation() assert sut.cookies == {} assert str(sut.cookies) == "" @@ -880,7 +886,7 @@ def test_cookies_mixin(): def test_cookies_mixin_path(): - sut = helpers.CookieMixin() + sut = CookieImplementation() assert sut.cookies == {} @@ -914,7 +920,7 @@ def test_cookies_mixin_path(): def test_sutonse_cookie__issue_del_cookie(): - sut = helpers.CookieMixin() + sut = CookieImplementation() assert sut.cookies == {} assert str(sut.cookies) == "" @@ -928,7 +934,7 @@ def test_sutonse_cookie__issue_del_cookie(): def test_cookie_set_after_del(): - sut = helpers.CookieMixin() + sut = CookieImplementation() sut.del_cookie("name") sut.set_cookie("name", "val") @@ -938,7 +944,7 @@ def test_cookie_set_after_del(): def test_populate_with_cookies(): - cookies_mixin = helpers.CookieMixin() + cookies_mixin = CookieImplementation() cookies_mixin.set_cookie("name", "value") headers = CIMultiDict()