Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/project/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
.........

Expand Down
19 changes: 17 additions & 2 deletions src/websockets/asyncio/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.

Expand Down Expand Up @@ -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")

Expand Down
10 changes: 10 additions & 0 deletions tests/asyncio/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -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([], "😀")
Expand Down