@@ -251,12 +251,13 @@ async def recv(self, decode: bool | None = None) -> Data:
251251
252252 You may override this behavior with the ``decode`` argument:
253253
254- * Set ``decode=False`` to disable UTF-8 decoding of Text_ frames
255- and return a bytestring (:class:`bytes`). This may be useful to
256- optimize performance when decoding isn't needed.
254+ * Set ``decode=False`` to disable UTF-8 decoding of Text_ frames and
255+ return a bytestring (:class:`bytes`). This improves performance
256+ when decoding isn't needed, for example if the message contains
257+ JSON and you're using a JSON library that expects a bytestring.
257258 * Set ``decode=True`` to force UTF-8 decoding of Binary_ frames
258- and return a string (:class:`str`). This is useful for servers
259- that send binary frames instead of text frames.
259+ and return a string (:class:`str`). This may be useful for
260+ servers that send binary frames instead of text frames.
260261
261262 Raises:
262263 ConnectionClosed: When the connection is closed.
@@ -333,7 +334,11 @@ async def recv_streaming(self, decode: bool | None = None) -> AsyncIterator[Data
333334 "is already running recv or recv_streaming"
334335 ) from None
335336
336- async def send (self , message : Data | Iterable [Data ] | AsyncIterable [Data ]) -> None :
337+ async def send (
338+ self ,
339+ message : Data | Iterable [Data ] | AsyncIterable [Data ],
340+ text : bool | None = None ,
341+ ) -> None :
337342 """
338343 Send a message.
339344
@@ -344,6 +349,17 @@ async def send(self, message: Data | Iterable[Data] | AsyncIterable[Data]) -> No
344349 .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6
345350 .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6
346351
352+ You may override this behavior with the ``text`` argument:
353+
354+ * Set ``text=True`` to send a bytestring or bytes-like object
355+ (:class:`bytes`, :class:`bytearray`, or :class:`memoryview`) as a
356+ Text_ frame. This improves performance when the message is already
357+ UTF-8 encoded, for example if the message contains JSON and you're
358+ using a JSON library that produces a bytestring.
359+ * Set ``text=False`` to send a string (:class:`str`) in a Binary_
360+ frame. This may be useful for servers that expect binary frames
361+ instead of text frames.
362+
347363 :meth:`send` also accepts an iterable or an asynchronous iterable of
348364 strings, bytestrings, or bytes-like objects to enable fragmentation_.
349365 Each item is treated as a message fragment and sent in its own frame.
@@ -393,12 +409,20 @@ async def send(self, message: Data | Iterable[Data] | AsyncIterable[Data]) -> No
393409 # strings and bytes-like objects are iterable.
394410
395411 if isinstance (message , str ):
396- async with self .send_context ():
397- self .protocol .send_text (message .encode ())
412+ if text is False :
413+ async with self .send_context ():
414+ self .protocol .send_binary (message .encode ())
415+ else :
416+ async with self .send_context ():
417+ self .protocol .send_text (message .encode ())
398418
399419 elif isinstance (message , BytesLike ):
400- async with self .send_context ():
401- self .protocol .send_binary (message )
420+ if text is True :
421+ async with self .send_context ():
422+ self .protocol .send_text (message )
423+ else :
424+ async with self .send_context ():
425+ self .protocol .send_binary (message )
402426
403427 # Catch a common mistake -- passing a dict to send().
404428
@@ -419,36 +443,32 @@ async def send(self, message: Data | Iterable[Data] | AsyncIterable[Data]) -> No
419443 try :
420444 # First fragment.
421445 if isinstance (chunk , str ):
422- text = True
423- async with self .send_context ():
424- self .protocol .send_text (
425- chunk .encode (),
426- fin = False ,
427- )
446+ if text is False :
447+ async with self .send_context ():
448+ self .protocol .send_binary (chunk .encode (), fin = False )
449+ else :
450+ async with self .send_context ():
451+ self .protocol .send_text (chunk .encode (), fin = False )
452+ encode = True
428453 elif isinstance (chunk , BytesLike ):
429- text = False
430- async with self .send_context ():
431- self .protocol .send_binary (
432- chunk ,
433- fin = False ,
434- )
454+ if text is True :
455+ async with self .send_context ():
456+ self .protocol .send_text (chunk , fin = False )
457+ else :
458+ async with self .send_context ():
459+ self .protocol .send_binary (chunk , fin = False )
460+ encode = False
435461 else :
436462 raise TypeError ("iterable must contain bytes or str" )
437463
438464 # Other fragments
439465 for chunk in chunks :
440- if isinstance (chunk , str ) and text :
466+ if isinstance (chunk , str ) and encode :
441467 async with self .send_context ():
442- self .protocol .send_continuation (
443- chunk .encode (),
444- fin = False ,
445- )
446- elif isinstance (chunk , BytesLike ) and not text :
468+ self .protocol .send_continuation (chunk .encode (), fin = False )
469+ elif isinstance (chunk , BytesLike ) and not encode :
447470 async with self .send_context ():
448- self .protocol .send_continuation (
449- chunk ,
450- fin = False ,
451- )
471+ self .protocol .send_continuation (chunk , fin = False )
452472 else :
453473 raise TypeError ("iterable must contain uniform types" )
454474
@@ -481,36 +501,32 @@ async def send(self, message: Data | Iterable[Data] | AsyncIterable[Data]) -> No
481501 try :
482502 # First fragment.
483503 if isinstance (chunk , str ):
484- text = True
485- async with self .send_context ():
486- self .protocol .send_text (
487- chunk .encode (),
488- fin = False ,
489- )
504+ if text is False :
505+ async with self .send_context ():
506+ self .protocol .send_binary (chunk .encode (), fin = False )
507+ else :
508+ async with self .send_context ():
509+ self .protocol .send_text (chunk .encode (), fin = False )
510+ encode = True
490511 elif isinstance (chunk , BytesLike ):
491- text = False
492- async with self .send_context ():
493- self .protocol .send_binary (
494- chunk ,
495- fin = False ,
496- )
512+ if text is True :
513+ async with self .send_context ():
514+ self .protocol .send_text (chunk , fin = False )
515+ else :
516+ async with self .send_context ():
517+ self .protocol .send_binary (chunk , fin = False )
518+ encode = False
497519 else :
498520 raise TypeError ("async iterable must contain bytes or str" )
499521
500522 # Other fragments
501523 async for chunk in achunks :
502- if isinstance (chunk , str ) and text :
524+ if isinstance (chunk , str ) and encode :
503525 async with self .send_context ():
504- self .protocol .send_continuation (
505- chunk .encode (),
506- fin = False ,
507- )
508- elif isinstance (chunk , BytesLike ) and not text :
526+ self .protocol .send_continuation (chunk .encode (), fin = False )
527+ elif isinstance (chunk , BytesLike ) and not encode :
509528 async with self .send_context ():
510- self .protocol .send_continuation (
511- chunk ,
512- fin = False ,
513- )
529+ self .protocol .send_continuation (chunk , fin = False )
514530 else :
515531 raise TypeError ("async iterable must contain uniform types" )
516532
0 commit comments