Skip to content

Commit 84859e1

Browse files
eugen-goebelaaugustin
authored andcommitted
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.
1 parent 1a38f5a commit 84859e1

3 files changed

Lines changed: 31 additions & 2 deletions

File tree

docs/project/changelog.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ Improvements
4242

4343
* Added wheels for ARMv7, PowerPC, RISC-V, and S/390.
4444

45+
* Added the ``text`` argument to :func:`~asyncio.server.broadcast`, mirroring
46+
:meth:`~asyncio.connection.Connection.send`, to force sending a Text or
47+
Binary frame regardless of the type of ``message``.
48+
4549
Bug fixes
4650
.........
4751

src/websockets/asyncio/connection.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,8 @@ def broadcast(
11481148
connections: Iterable[Connection],
11491149
message: DataLike,
11501150
raise_exceptions: bool = False,
1151+
*,
1152+
text: bool | None = None,
11511153
) -> None:
11521154
"""
11531155
Broadcast a message to several WebSocket connections.
@@ -1159,6 +1161,17 @@ def broadcast(
11591161
.. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6
11601162
.. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6
11611163
1164+
You may override this behavior with the ``text`` argument:
1165+
1166+
* Set ``text=True`` to send an UTF-8 bytestring or bytes-like object
1167+
(:class:`bytes`, :class:`bytearray`, or :class:`memoryview`) in a
1168+
Text_ frame. This improves performance when the message is already
1169+
UTF-8 encoded, for example if the message contains JSON and you're
1170+
using a JSON library that produces a bytestring.
1171+
* Set ``text=False`` to send a string (:class:`str`) in a Binary_
1172+
frame. This may be useful for servers that expect binary frames
1173+
instead of text frames.
1174+
11621175
:func:`broadcast` pushes the message synchronously to all connections even
11631176
if their write buffers are overflowing. There's no backpressure.
11641177
@@ -1189,16 +1202,18 @@ def broadcast(
11891202
websockets: WebSocket connections to which the message will be sent.
11901203
message: Message to send.
11911204
raise_exceptions: Whether to raise an exception in case of failures.
1205+
text: Send ``message`` in a Text_ frame if :obj:`True`, in a Binary_
1206+
frame if :obj:`False`, or according to its type if :obj:`None`.
11921207
11931208
Raises:
11941209
TypeError: If ``message`` doesn't have a supported type.
11951210
11961211
"""
11971212
if isinstance(message, str):
1198-
send_method = "send_text"
1213+
send_method = "send_binary" if text is False else "send_text"
11991214
message = message.encode()
12001215
elif isinstance(message, BytesLike):
1201-
send_method = "send_binary"
1216+
send_method = "send_text" if text is True else "send_binary"
12021217
else:
12031218
raise TypeError("data must be str or bytes")
12041219

tests/asyncio/test_connection.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,16 @@ async def test_broadcast_binary_reports_no_errors(self):
13361336
broadcast([self.connection], b"\x01\x02\xfe\xff", raise_exceptions=True)
13371337
await self.assertFrameSent(Frame(Opcode.BINARY, b"\x01\x02\xfe\xff"))
13381338

1339+
async def test_broadcast_text_from_bytes(self):
1340+
"""broadcast broadcasts a text message from bytes."""
1341+
broadcast([self.connection], "😀".encode(), text=True)
1342+
await self.assertFrameSent(Frame(Opcode.TEXT, "😀".encode()))
1343+
1344+
async def test_broadcast_binary_from_str(self):
1345+
"""broadcast broadcasts a binary message from a str."""
1346+
broadcast([self.connection], "😀", text=False)
1347+
await self.assertFrameSent(Frame(Opcode.BINARY, "😀".encode()))
1348+
13391349
async def test_broadcast_no_clients(self):
13401350
"""broadcast does nothing when called with an empty list of clients."""
13411351
broadcast([], "😀")

0 commit comments

Comments
 (0)