diff --git a/.gitignore b/.gitignore index 69f52e10d87..8556509c6f7 100644 --- a/.gitignore +++ b/.gitignore @@ -34,8 +34,6 @@ .vimrc .vscode aiohttp/_find_header.c -aiohttp/_frozenlist.c -aiohttp/_frozenlist.html aiohttp/_headers.html aiohttp/_headers.pxi aiohttp/_helpers.c diff --git a/CHANGES/5293.feature b/CHANGES/5293.feature new file mode 100644 index 00000000000..fced7ed5aca --- /dev/null +++ b/CHANGES/5293.feature @@ -0,0 +1 @@ +Switch to external frozenlist and aiosignal libraries. diff --git a/aiohttp/__init__.py b/aiohttp/__init__.py index b36d935df7b..f7f8910c0f5 100644 --- a/aiohttp/__init__.py +++ b/aiohttp/__init__.py @@ -78,7 +78,6 @@ DefaultResolver as DefaultResolver, ThreadedResolver as ThreadedResolver, ) -from .signals import Signal as Signal from .streams import ( EMPTY_PAYLOAD as EMPTY_PAYLOAD, DataQueue as DataQueue, diff --git a/aiohttp/_frozenlist.pyx b/aiohttp/_frozenlist.pyx deleted file mode 100644 index b1305772f4b..00000000000 --- a/aiohttp/_frozenlist.pyx +++ /dev/null @@ -1,108 +0,0 @@ -from collections.abc import MutableSequence - - -cdef class FrozenList: - - cdef readonly bint frozen - cdef list _items - - def __init__(self, items=None): - self.frozen = False - if items is not None: - items = list(items) - else: - items = [] - self._items = items - - cdef object _check_frozen(self): - if self.frozen: - raise RuntimeError("Cannot modify frozen list.") - - cdef inline object _fast_len(self): - return len(self._items) - - def freeze(self): - self.frozen = True - - def __getitem__(self, index): - return self._items[index] - - def __setitem__(self, index, value): - self._check_frozen() - self._items[index] = value - - def __delitem__(self, index): - self._check_frozen() - del self._items[index] - - def __len__(self): - return self._fast_len() - - def __iter__(self): - return self._items.__iter__() - - def __reversed__(self): - return self._items.__reversed__() - - def __richcmp__(self, other, op): - if op == 0: # < - return list(self) < other - if op == 1: # <= - return list(self) <= other - if op == 2: # == - return list(self) == other - if op == 3: # != - return list(self) != other - if op == 4: # > - return list(self) > other - if op == 5: # => - return list(self) >= other - - def insert(self, pos, item): - self._check_frozen() - self._items.insert(pos, item) - - def __contains__(self, item): - return item in self._items - - def __iadd__(self, items): - self._check_frozen() - self._items += list(items) - return self - - def index(self, item): - return self._items.index(item) - - def remove(self, item): - self._check_frozen() - self._items.remove(item) - - def clear(self): - self._check_frozen() - self._items.clear() - - def extend(self, items): - self._check_frozen() - self._items += list(items) - - def reverse(self): - self._check_frozen() - self._items.reverse() - - def pop(self, index=-1): - self._check_frozen() - return self._items.pop(index) - - def append(self, item): - self._check_frozen() - return self._items.append(item) - - def count(self, item): - return self._items.count(item) - - def __repr__(self): - return ''.format(self.frozen, - self._items) - - -MutableSequence.register(FrozenList) diff --git a/aiohttp/frozenlist.py b/aiohttp/frozenlist.py deleted file mode 100644 index 46b26108cfa..00000000000 --- a/aiohttp/frozenlist.py +++ /dev/null @@ -1,72 +0,0 @@ -from collections.abc import MutableSequence -from functools import total_ordering - -from .helpers import NO_EXTENSIONS - - -@total_ordering -class FrozenList(MutableSequence): - - __slots__ = ("_frozen", "_items") - - def __init__(self, items=None): - self._frozen = False - if items is not None: - items = list(items) - else: - items = [] - self._items = items - - @property - def frozen(self): - return self._frozen - - def freeze(self): - self._frozen = True - - def __getitem__(self, index): - return self._items[index] - - def __setitem__(self, index, value): - if self._frozen: - raise RuntimeError("Cannot modify frozen list.") - self._items[index] = value - - def __delitem__(self, index): - if self._frozen: - raise RuntimeError("Cannot modify frozen list.") - del self._items[index] - - def __len__(self): - return self._items.__len__() - - def __iter__(self): - return self._items.__iter__() - - def __reversed__(self): - return self._items.__reversed__() - - def __eq__(self, other): - return list(self) == other - - def __le__(self, other): - return list(self) <= other - - def insert(self, pos, item): - if self._frozen: - raise RuntimeError("Cannot modify frozen list.") - self._items.insert(pos, item) - - def __repr__(self): - return f"" - - -PyFrozenList = FrozenList - -try: - from aiohttp._frozenlist import FrozenList as CFrozenList # type: ignore - - if not NO_EXTENSIONS: - FrozenList = CFrozenList # type: ignore -except ImportError: # pragma: no cover - pass diff --git a/aiohttp/frozenlist.pyi b/aiohttp/frozenlist.pyi deleted file mode 100644 index 72ab086715b..00000000000 --- a/aiohttp/frozenlist.pyi +++ /dev/null @@ -1,46 +0,0 @@ -from typing import ( - Generic, - Iterable, - Iterator, - List, - MutableSequence, - Optional, - TypeVar, - Union, - overload, -) - -_T = TypeVar("_T") -_Arg = Union[List[_T], Iterable[_T]] - -class FrozenList(MutableSequence[_T], Generic[_T]): - def __init__(self, items: Optional[_Arg[_T]] = ...) -> None: ... - @property - def frozen(self) -> bool: ... - def freeze(self) -> None: ... - @overload - def __getitem__(self, i: int) -> _T: ... - @overload - def __getitem__(self, s: slice) -> FrozenList[_T]: ... - @overload - def __setitem__(self, i: int, o: _T) -> None: ... - @overload - def __setitem__(self, s: slice, o: Iterable[_T]) -> None: ... - @overload - def __delitem__(self, i: int) -> None: ... - @overload - def __delitem__(self, i: slice) -> None: ... - def __len__(self) -> int: ... - def __iter__(self) -> Iterator[_T]: ... - def __reversed__(self) -> Iterator[_T]: ... - def __eq__(self, other: object) -> bool: ... - def __le__(self, other: FrozenList[_T]) -> bool: ... - def __ne__(self, other: object) -> bool: ... - def __lt__(self, other: FrozenList[_T]) -> bool: ... - def __ge__(self, other: FrozenList[_T]) -> bool: ... - def __gt__(self, other: FrozenList[_T]) -> bool: ... - def insert(self, pos: int, item: _T) -> None: ... - def __repr__(self) -> str: ... - -# types for C accelerators are the same -CFrozenList = PyFrozenList = FrozenList diff --git a/aiohttp/signals.py b/aiohttp/signals.py deleted file mode 100644 index d406c02423b..00000000000 --- a/aiohttp/signals.py +++ /dev/null @@ -1,34 +0,0 @@ -from aiohttp.frozenlist import FrozenList - -__all__ = ("Signal",) - - -class Signal(FrozenList): - """Coroutine-based signal implementation. - - To connect a callback to a signal, use any list method. - - Signals are fired using the send() coroutine, which takes named - arguments. - """ - - __slots__ = ("_owner",) - - def __init__(self, owner): - super().__init__() - self._owner = owner - - def __repr__(self): - return "".format( - self._owner, self.frozen, list(self) - ) - - async def send(self, *args, **kwargs): - """ - Sends data to all registered receivers. - """ - if not self.frozen: - raise RuntimeError("Cannot send non-frozen signal.") - - for receiver in self: - await receiver(*args, **kwargs) # type: ignore diff --git a/aiohttp/signals.pyi b/aiohttp/signals.pyi deleted file mode 100644 index 455f8e2f227..00000000000 --- a/aiohttp/signals.pyi +++ /dev/null @@ -1,12 +0,0 @@ -from typing import Any, Generic, TypeVar - -from aiohttp.frozenlist import FrozenList - -__all__ = ("Signal",) - -_T = TypeVar("_T") - -class Signal(FrozenList[_T], Generic[_T]): - def __init__(self, owner: Any) -> None: ... - def __repr__(self) -> str: ... - async def send(self, *args: Any, **kwargs: Any) -> None: ... diff --git a/aiohttp/test_utils.py b/aiohttp/test_utils.py index 3923f2b8c76..1fd8bcac68a 100644 --- a/aiohttp/test_utils.py +++ b/aiohttp/test_utils.py @@ -13,6 +13,7 @@ from typing import TYPE_CHECKING, Any, Callable, Iterator, List, Optional, Type, Union from unittest import mock +from aiosignal import Signal from multidict import CIMultiDict, CIMultiDictProxy from yarl import URL @@ -25,7 +26,6 @@ from .client_ws import ClientWebSocketResponse from .helpers import PY_38, sentinel from .http import HttpVersion, RawRequestMessage -from .signals import Signal from .web import ( Application, AppRunner, diff --git a/aiohttp/tracing.py b/aiohttp/tracing.py index 26261df3b04..4b04b67f28e 100644 --- a/aiohttp/tracing.py +++ b/aiohttp/tracing.py @@ -2,11 +2,11 @@ from typing import TYPE_CHECKING, Awaitable, Optional, Type, TypeVar import attr +from aiosignal import Signal from multidict import CIMultiDict from yarl import URL from .client_reqrep import ClientResponse -from .signals import Signal if TYPE_CHECKING: # pragma: no cover from typing_extensions import Protocol diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py index 14f2937ae55..68953ac0776 100644 --- a/aiohttp/web_app.py +++ b/aiohttp/web_app.py @@ -22,6 +22,9 @@ cast, ) +from aiosignal import Signal +from frozenlist import FrozenList + from . import hdrs from .abc import ( AbstractAccessLogger, @@ -29,11 +32,9 @@ AbstractRouter, AbstractStreamWriter, ) -from .frozenlist import FrozenList from .helpers import DEBUG from .http_parser import RawRequestMessage from .log import web_logger -from .signals import Signal from .streams import StreamReader from .web_log import AccessLogger from .web_middlewares import _fix_request_current_app diff --git a/requirements/base.txt b/requirements/base.txt index 163d8703671..ee3885503e7 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,6 +1,7 @@ -r multidict.txt # required c-ares will not build on windows and has build problems on Macos Python<3.7 aiodns==2.0.0; sys_platform=="linux" or sys_platform=="darwin" and python_version>="3.7" +aiosignal==1.1.2 async-generator==1.10 async-timeout==3.0.1 asynctest==0.13.0; python_version<"3.8" @@ -8,6 +9,7 @@ attrs==20.3.0 brotlipy==0.7.0 cchardet==2.1.7 chardet==3.0.4 +frozenlist==1.1.1 gunicorn==20.0.4 idna-ssl==1.1.0; python_version<"3.7" typing_extensions==3.7.4.3 diff --git a/setup.py b/setup.py index e61f86b4b73..3cffebad395 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,6 @@ ], define_macros=[("HTTP_PARSER_STRICT", 0)], ), - Extension("aiohttp._frozenlist", ["aiohttp/_frozenlist.c"]), Extension("aiohttp._helpers", ["aiohttp/_helpers.c"]), Extension("aiohttp._http_writer", ["aiohttp/_http_writer.c"]), ] @@ -71,7 +70,9 @@ def build_extension(self, ext): 'asynctest==0.13.0; python_version<"3.8"', "yarl>=1.0,<2.0", 'idna-ssl>=1.0; python_version<"3.7"', - "typing_extensions>=3.6.5", + "typing_extensions>=3.7.4", + "frozenlist>=1.1.1", + "aiosignal>=1.1.2", ] diff --git a/tests/test_frozenlist.py b/tests/test_frozenlist.py deleted file mode 100644 index 68241a2c38f..00000000000 --- a/tests/test_frozenlist.py +++ /dev/null @@ -1,230 +0,0 @@ -from collections.abc import MutableSequence - -import pytest - -from aiohttp.frozenlist import FrozenList, PyFrozenList - - -class FrozenListMixin: - FrozenList = NotImplemented - - SKIP_METHODS = {"__abstractmethods__", "__slots__"} - - def test_subclass(self) -> None: - assert issubclass(self.FrozenList, MutableSequence) - - def test_iface(self) -> None: - for name in set(dir(MutableSequence)) - self.SKIP_METHODS: - if ( - name.startswith("_") and not name.endswith("_") - ) or name == "__class_getitem__": - continue - assert hasattr(self.FrozenList, name) - - def test_ctor_default(self) -> None: - _list = self.FrozenList([]) - assert not _list.frozen - - def test_ctor(self) -> None: - _list = self.FrozenList([1]) - assert not _list.frozen - - def test_ctor_copy_list(self) -> None: - orig = [1] - _list = self.FrozenList(orig) - del _list[0] - assert _list != orig - - def test_freeze(self) -> None: - _list = self.FrozenList() - _list.freeze() - assert _list.frozen - - def test_repr(self) -> None: - _list = self.FrozenList([1]) - assert repr(_list) == "" - _list.freeze() - assert repr(_list) == "" - - def test_getitem(self) -> None: - _list = self.FrozenList([1, 2]) - assert _list[1] == 2 - - def test_setitem(self) -> None: - _list = self.FrozenList([1, 2]) - _list[1] = 3 - assert _list[1] == 3 - - def test_delitem(self) -> None: - _list = self.FrozenList([1, 2]) - del _list[0] - assert len(_list) == 1 - assert _list[0] == 2 - - def test_len(self) -> None: - _list = self.FrozenList([1]) - assert len(_list) == 1 - - def test_iter(self) -> None: - _list = self.FrozenList([1, 2]) - assert list(iter(_list)) == [1, 2] - - def test_reversed(self) -> None: - _list = self.FrozenList([1, 2]) - assert list(reversed(_list)) == [2, 1] - - def test_eq(self) -> None: - _list = self.FrozenList([1]) - assert _list == [1] - - def test_ne(self) -> None: - _list = self.FrozenList([1]) - assert _list != [2] - - def test_le(self) -> None: - _list = self.FrozenList([1]) - assert _list <= [1] - - def test_lt(self) -> None: - _list = self.FrozenList([1]) - assert _list <= [3] - - def test_ge(self) -> None: - _list = self.FrozenList([1]) - assert _list >= [1] - - def test_gt(self) -> None: - _list = self.FrozenList([2]) - assert _list > [1] - - def test_insert(self) -> None: - _list = self.FrozenList([2]) - _list.insert(0, 1) - assert _list == [1, 2] - - def test_frozen_setitem(self) -> None: - _list = self.FrozenList([1]) - _list.freeze() - with pytest.raises(RuntimeError): - _list[0] = 2 - - def test_frozen_delitem(self) -> None: - _list = self.FrozenList([1]) - _list.freeze() - with pytest.raises(RuntimeError): - del _list[0] - - def test_frozen_insert(self) -> None: - _list = self.FrozenList([1]) - _list.freeze() - with pytest.raises(RuntimeError): - _list.insert(0, 2) - - def test_contains(self) -> None: - _list = self.FrozenList([2]) - assert 2 in _list - - def test_iadd(self) -> None: - _list = self.FrozenList([1]) - _list += [2] - assert _list == [1, 2] - - def test_iadd_frozen(self) -> None: - _list = self.FrozenList([1]) - _list.freeze() - with pytest.raises(RuntimeError): - _list += [2] - assert _list == [1] - - def test_index(self) -> None: - _list = self.FrozenList([1]) - assert _list.index(1) == 0 - - def test_remove(self) -> None: - _list = self.FrozenList([1]) - _list.remove(1) - assert len(_list) == 0 - - def test_remove_frozen(self) -> None: - _list = self.FrozenList([1]) - _list.freeze() - with pytest.raises(RuntimeError): - _list.remove(1) - assert _list == [1] - - def test_clear(self) -> None: - _list = self.FrozenList([1]) - _list.clear() - assert len(_list) == 0 - - def test_clear_frozen(self) -> None: - _list = self.FrozenList([1]) - _list.freeze() - with pytest.raises(RuntimeError): - _list.clear() - assert _list == [1] - - def test_extend(self) -> None: - _list = self.FrozenList([1]) - _list.extend([2]) - assert _list == [1, 2] - - def test_extend_frozen(self) -> None: - _list = self.FrozenList([1]) - _list.freeze() - with pytest.raises(RuntimeError): - _list.extend([2]) - assert _list == [1] - - def test_reverse(self) -> None: - _list = self.FrozenList([1, 2]) - _list.reverse() - assert _list == [2, 1] - - def test_reverse_frozen(self) -> None: - _list = self.FrozenList([1, 2]) - _list.freeze() - with pytest.raises(RuntimeError): - _list.reverse() - assert _list == [1, 2] - - def test_pop(self) -> None: - _list = self.FrozenList([1, 2]) - assert _list.pop(0) == 1 - assert _list == [2] - - def test_pop_default(self) -> None: - _list = self.FrozenList([1, 2]) - assert _list.pop() == 2 - assert _list == [1] - - def test_pop_frozen(self) -> None: - _list = self.FrozenList([1, 2]) - _list.freeze() - with pytest.raises(RuntimeError): - _list.pop() - assert _list == [1, 2] - - def test_append(self) -> None: - _list = self.FrozenList([1, 2]) - _list.append(3) - assert _list == [1, 2, 3] - - def test_append_frozen(self) -> None: - _list = self.FrozenList([1, 2]) - _list.freeze() - with pytest.raises(RuntimeError): - _list.append(3) - assert _list == [1, 2] - - def test_count(self) -> None: - _list = self.FrozenList([1, 2]) - assert _list.count(1) == 1 - - -class TestFrozenList(FrozenListMixin): - FrozenList = FrozenList - - -class TestFrozenListPy(FrozenListMixin): - FrozenList = PyFrozenList diff --git a/tests/test_signals.py b/tests/test_signals.py deleted file mode 100644 index 971cab5c448..00000000000 --- a/tests/test_signals.py +++ /dev/null @@ -1,167 +0,0 @@ -from unittest import mock - -import pytest -from multidict import CIMultiDict -from re_assert import Matches - -from aiohttp.signals import Signal -from aiohttp.test_utils import make_mocked_coro, make_mocked_request -from aiohttp.web import Application, Response - - -@pytest.fixture -def app(): - return Application() - - -def make_request(app, method, path, headers=CIMultiDict()): - return make_mocked_request(method, path, headers, app=app) - - -async def test_add_signal_handler_not_a_callable(app) -> None: - callback = True - app.on_response_prepare.append(callback) - app.on_response_prepare.freeze() - with pytest.raises(TypeError): - await app.on_response_prepare(None, None) - - -async def test_function_signal_dispatch(app) -> None: - signal = Signal(app) - kwargs = {"foo": 1, "bar": 2} - - callback_mock = mock.Mock() - - async def callback(**kwargs): - callback_mock(**kwargs) - - signal.append(callback) - signal.freeze() - - await signal.send(**kwargs) - callback_mock.assert_called_once_with(**kwargs) - - -async def test_function_signal_dispatch2(app) -> None: - signal = Signal(app) - args = {"a", "b"} - kwargs = {"foo": 1, "bar": 2} - - callback_mock = mock.Mock() - - async def callback(*args, **kwargs): - callback_mock(*args, **kwargs) - - signal.append(callback) - signal.freeze() - - await signal.send(*args, **kwargs) - callback_mock.assert_called_once_with(*args, **kwargs) - - -async def test_response_prepare(app) -> None: - callback = mock.Mock() - - async def cb(*args, **kwargs): - callback(*args, **kwargs) - - app.on_response_prepare.append(cb) - app.on_response_prepare.freeze() - - request = make_request(app, "GET", "/") - response = Response(body=b"") - await response.prepare(request) - - callback.assert_called_once_with(request, response) - - -async def test_non_coroutine(app) -> None: - signal = Signal(app) - kwargs = {"foo": 1, "bar": 2} - - callback = mock.Mock() - - signal.append(callback) - signal.freeze() - - with pytest.raises(TypeError): - await signal.send(**kwargs) - - -def test_setitem(app) -> None: - signal = Signal(app) - m1 = mock.Mock() - signal.append(m1) - assert signal[0] is m1 - m2 = mock.Mock() - signal[0] = m2 - assert signal[0] is m2 - - -def test_delitem(app) -> None: - signal = Signal(app) - m1 = mock.Mock() - signal.append(m1) - assert len(signal) == 1 - del signal[0] - assert len(signal) == 0 - - -def test_cannot_append_to_frozen_signal(app) -> None: - signal = Signal(app) - m1 = mock.Mock() - m2 = mock.Mock() - signal.append(m1) - signal.freeze() - with pytest.raises(RuntimeError): - signal.append(m2) - - assert list(signal) == [m1] - - -def test_cannot_setitem_in_frozen_signal(app) -> None: - signal = Signal(app) - m1 = mock.Mock() - m2 = mock.Mock() - signal.append(m1) - signal.freeze() - with pytest.raises(RuntimeError): - signal[0] = m2 - - assert list(signal) == [m1] - - -def test_cannot_delitem_in_frozen_signal(app) -> None: - signal = Signal(app) - m1 = mock.Mock() - signal.append(m1) - signal.freeze() - with pytest.raises(RuntimeError): - del signal[0] - - assert list(signal) == [m1] - - -async def test_cannot_send_non_frozen_signal(app) -> None: - signal = Signal(app) - - callback = make_mocked_coro() - - signal.append(callback) - - with pytest.raises(RuntimeError): - await signal.send() - - assert not callback.called - - -async def test_repr(app) -> None: - signal = Signal(app) - - callback = make_mocked_coro() - - signal.append(callback) - - assert Matches( - r", frozen=False, " r"\[\]>" - ) == repr(signal) diff --git a/tests/test_web_exceptions.py b/tests/test_web_exceptions.py index 43e5029803f..33b5a4f240a 100644 --- a/tests/test_web_exceptions.py +++ b/tests/test_web_exceptions.py @@ -3,9 +3,10 @@ from traceback import format_exception from unittest import mock +import aiosignal import pytest -from aiohttp import helpers, signals, web +from aiohttp import helpers, web from aiohttp.test_utils import make_mocked_request @@ -41,7 +42,7 @@ async def write_headers(status_line, headers): app = mock.Mock() app._debug = False - app.on_response_prepare = signals.Signal(app) + app.on_response_prepare = aiosignal.Signal(app) app.on_response_prepare.freeze() req = make_mocked_request(method, path, app=app, writer=writer) return req diff --git a/tests/test_web_response.py b/tests/test_web_response.py index f8473431010..a26d548bc16 100644 --- a/tests/test_web_response.py +++ b/tests/test_web_response.py @@ -5,11 +5,12 @@ from concurrent.futures import ThreadPoolExecutor from unittest import mock +import aiosignal import pytest from multidict import CIMultiDict, CIMultiDictProxy from re_assert import Matches -from aiohttp import HttpVersion, HttpVersion10, HttpVersion11, hdrs, signals +from aiohttp import HttpVersion, HttpVersion10, HttpVersion11, hdrs from aiohttp.payload import BytesPayload from aiohttp.test_utils import make_mocked_coro, make_mocked_request from aiohttp.web import ContentCoding, Response, StreamResponse, json_response @@ -26,7 +27,7 @@ def make_request( app = kwargs.pop("app", None) or mock.Mock() app._debug = False if on_response_prepare is None: - on_response_prepare = signals.Signal(app) + on_response_prepare = aiosignal.Signal(app) app.on_response_prepare = on_response_prepare app.on_response_prepare.freeze() protocol = kwargs.pop("protocol", None) or mock.Mock() @@ -820,7 +821,7 @@ async def test_prepare_twice() -> None: async def test_prepare_calls_signal() -> None: app = mock.Mock() sig = make_mocked_coro() - on_response_prepare = signals.Signal(app) + on_response_prepare = aiosignal.Signal(app) on_response_prepare.append(sig) req = make_request("GET", "/", app=app, on_response_prepare=on_response_prepare) resp = StreamResponse() @@ -1184,7 +1185,7 @@ async def _strip_server(req, res): del res.headers["Server"] app = mock.Mock() - sig = signals.Signal(app) + sig = aiosignal.Signal(app) sig.append(_strip_server) req = make_request("GET", "/", on_response_prepare=sig, app=app) diff --git a/tests/test_web_websocket.py b/tests/test_web_websocket.py index 0a79113537e..ee8ae2d39a0 100644 --- a/tests/test_web_websocket.py +++ b/tests/test_web_websocket.py @@ -1,10 +1,11 @@ import asyncio from unittest import mock +import aiosignal import pytest from multidict import CIMultiDict -from aiohttp import WSMessage, WSMsgType, signals +from aiohttp import WSMessage, WSMsgType from aiohttp.streams import EofStream from aiohttp.test_utils import make_mocked_coro, make_mocked_request from aiohttp.web import HTTPBadRequest, WebSocketResponse @@ -16,7 +17,7 @@ def app(loop): ret = mock.Mock() ret.loop = loop ret._debug = False - ret.on_response_prepare = signals.Signal(ret) + ret.on_response_prepare = aiosignal.Signal(ret) ret.on_response_prepare.freeze() return ret