From a3e1b115e39919b6f7f50b298246c68b8b12d90a Mon Sep 17 00:00:00 2001 From: Eugen Goebel Date: Sat, 20 Jun 2026 20:51:37 +0200 Subject: [PATCH] Add text argument to broadcast() to force the frame type Closes #1694. broadcast() now accepts a keyword-only text argument that mirrors Connection.send(): set text=True to send a bytestring in a Text frame, or text=False to send a str in a Binary frame. This lets callers control the frame type without converting the message first, for example broadcasting an already UTF-8 encoded payload as a text frame. Based on the proposal and patch by @lampeh in #1694. --- docs/project/changelog.rst | 4 ++++ src/websockets/asyncio/connection.py | 19 +++++++++++++++++-- tests/asyncio/test_connection.py | 10 ++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/docs/project/changelog.rst b/docs/project/changelog.rst index 0c96d654..2582100c 100644 --- a/docs/project/changelog.rst +++ b/docs/project/changelog.rst @@ -42,6 +42,10 @@ Improvements * Added wheels for ARMv7, PowerPC, RISC-V, and S/390. +* Added the ``text`` argument to :func:`~asyncio.server.broadcast`, mirroring + :meth:`~asyncio.connection.Connection.send`, to force sending a Text or + Binary frame regardless of the type of ``message``. + Bug fixes ......... diff --git a/src/websockets/asyncio/connection.py b/src/websockets/asyncio/connection.py index 205a2be5..14029256 100644 --- a/src/websockets/asyncio/connection.py +++ b/src/websockets/asyncio/connection.py @@ -1148,6 +1148,8 @@ def broadcast( connections: Iterable[Connection], message: DataLike, raise_exceptions: bool = False, + *, + text: bool | None = None, ) -> None: """ Broadcast a message to several WebSocket connections. @@ -1159,6 +1161,17 @@ def broadcast( .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 + You may override this behavior with the ``text`` argument: + + * Set ``text=True`` to send an UTF-8 bytestring or bytes-like object + (:class:`bytes`, :class:`bytearray`, or :class:`memoryview`) in a + Text_ frame. This improves performance when the message is already + UTF-8 encoded, for example if the message contains JSON and you're + using a JSON library that produces a bytestring. + * Set ``text=False`` to send a string (:class:`str`) in a Binary_ + frame. This may be useful for servers that expect binary frames + instead of text frames. + :func:`broadcast` pushes the message synchronously to all connections even if their write buffers are overflowing. There's no backpressure. @@ -1189,16 +1202,18 @@ def broadcast( websockets: WebSocket connections to which the message will be sent. message: Message to send. raise_exceptions: Whether to raise an exception in case of failures. + text: Send ``message`` in a Text_ frame if :obj:`True`, in a Binary_ + frame if :obj:`False`, or according to its type if :obj:`None`. Raises: TypeError: If ``message`` doesn't have a supported type. """ if isinstance(message, str): - send_method = "send_text" + send_method = "send_binary" if text is False else "send_text" message = message.encode() elif isinstance(message, BytesLike): - send_method = "send_binary" + send_method = "send_text" if text is True else "send_binary" else: raise TypeError("data must be str or bytes") diff --git a/tests/asyncio/test_connection.py b/tests/asyncio/test_connection.py index f51f7a64..76941a13 100644 --- a/tests/asyncio/test_connection.py +++ b/tests/asyncio/test_connection.py @@ -1336,6 +1336,16 @@ async def test_broadcast_binary_reports_no_errors(self): broadcast([self.connection], b"\x01\x02\xfe\xff", raise_exceptions=True) await self.assertFrameSent(Frame(Opcode.BINARY, b"\x01\x02\xfe\xff")) + async def test_broadcast_text_from_bytes(self): + """broadcast broadcasts a text message from bytes.""" + broadcast([self.connection], "😀".encode(), text=True) + await self.assertFrameSent(Frame(Opcode.TEXT, "😀".encode())) + + async def test_broadcast_binary_from_str(self): + """broadcast broadcasts a binary message from a str.""" + broadcast([self.connection], "😀", text=False) + await self.assertFrameSent(Frame(Opcode.BINARY, "😀".encode())) + async def test_broadcast_no_clients(self): """broadcast does nothing when called with an empty list of clients.""" broadcast([], "😀")