From f605e86aeab7ac045c21e2eac0d677916031022e Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 20 Feb 2022 21:28:28 +0100 Subject: [PATCH] Restore backwards-compatibility for partial handlers. Fix #1095. --- docs/project/changelog.rst | 3 +++ src/websockets/legacy/server.py | 28 +++++++++++++++++++--------- tests/legacy/test_client_server.py | 17 +++++++++++++++++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/docs/project/changelog.rst b/docs/project/changelog.rst index 3c264a31b..b47ae2341 100644 --- a/docs/project/changelog.rst +++ b/docs/project/changelog.rst @@ -38,6 +38,9 @@ Improvements Bug fixes ......... +* Fixed backwards-incompatibility in 10.1 for connection handlers created with + :func:`functools.partial`. + * Avoided leaking open sockets when :func:`~client.connect` is canceled. 10.1 diff --git a/src/websockets/legacy/server.py b/src/websockets/legacy/server.py index 8bc466a38..ea5b0d1fa 100644 --- a/src/websockets/legacy/server.py +++ b/src/websockets/legacy/server.py @@ -1126,17 +1126,27 @@ def remove_path_argument( Callable[[WebSocketServerProtocol, str], Awaitable[Any]], ] ) -> Callable[[WebSocketServerProtocol], Awaitable[Any]]: - if len(inspect.signature(ws_handler).parameters) == 2: - # Enable deprecation warning and announce deprecation in 11.0. - # warnings.warn("remove second argument of ws_handler", DeprecationWarning) + try: + inspect.signature(ws_handler).bind(None) + except TypeError: + try: + inspect.signature(ws_handler).bind(None, "") + except TypeError: # pragma: no cover + # ws_handler accepts neither one nor two arguments; leave it alone. + pass + else: + # ws_handler accepts two arguments; activate backwards compatibility. + + # Enable deprecation warning and announce deprecation in 11.0. + # warnings.warn("remove second argument of ws_handler", DeprecationWarning) - async def _ws_handler(websocket: WebSocketServerProtocol) -> Any: - return await cast( - Callable[[WebSocketServerProtocol, str], Awaitable[Any]], - ws_handler, - )(websocket, websocket.path) + async def _ws_handler(websocket: WebSocketServerProtocol) -> Any: + return await cast( + Callable[[WebSocketServerProtocol, str], Awaitable[Any]], + ws_handler, + )(websocket, websocket.path) - return _ws_handler + return _ws_handler return cast( Callable[[WebSocketServerProtocol], Awaitable[Any]], diff --git a/tests/legacy/test_client_server.py b/tests/legacy/test_client_server.py index e6fb05d57..acc231d76 100644 --- a/tests/legacy/test_client_server.py +++ b/tests/legacy/test_client_server.py @@ -497,6 +497,23 @@ async def handler_with_path(ws, path): "/path", ) + def test_ws_handler_argument_backwards_compatibility_partial(self): + async def handler_with_path(ws, path, extra): + await ws.send(path) + + bound_handler_with_path = functools.partial(handler_with_path, extra=None) + + with self.temp_server( + handler=bound_handler_with_path, + # Enable deprecation warning and announce deprecation in 11.0. + # deprecation_warnings=["remove second argument of ws_handler"], + ): + with self.temp_client("/path"): + self.assertEqual( + self.loop.run_until_complete(self.client.recv()), + "/path", + ) + async def process_request_OK(path, request_headers): return http.HTTPStatus.OK, [], b"OK\n"