@@ -251,12 +251,13 @@ async def recv(self, decode: bool | None = None) -> Data:
251
251
252
252
You may override this behavior with the ``decode`` argument:
253
253
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.
257
258
* 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.
260
261
261
262
Raises:
262
263
ConnectionClosed: When the connection is closed.
@@ -333,7 +334,11 @@ async def recv_streaming(self, decode: bool | None = None) -> AsyncIterator[Data
333
334
"is already running recv or recv_streaming"
334
335
) from None
335
336
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 :
337
342
"""
338
343
Send a message.
339
344
@@ -344,6 +349,17 @@ async def send(self, message: Data | Iterable[Data] | AsyncIterable[Data]) -> No
344
349
.. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6
345
350
.. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6
346
351
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
+
347
363
:meth:`send` also accepts an iterable or an asynchronous iterable of
348
364
strings, bytestrings, or bytes-like objects to enable fragmentation_.
349
365
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
393
409
# strings and bytes-like objects are iterable.
394
410
395
411
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 ())
398
418
399
419
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 )
402
426
403
427
# Catch a common mistake -- passing a dict to send().
404
428
@@ -419,36 +443,32 @@ async def send(self, message: Data | Iterable[Data] | AsyncIterable[Data]) -> No
419
443
try :
420
444
# First fragment.
421
445
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
428
453
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
435
461
else :
436
462
raise TypeError ("iterable must contain bytes or str" )
437
463
438
464
# Other fragments
439
465
for chunk in chunks :
440
- if isinstance (chunk , str ) and text :
466
+ if isinstance (chunk , str ) and encode :
441
467
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 :
447
470
async with self .send_context ():
448
- self .protocol .send_continuation (
449
- chunk ,
450
- fin = False ,
451
- )
471
+ self .protocol .send_continuation (chunk , fin = False )
452
472
else :
453
473
raise TypeError ("iterable must contain uniform types" )
454
474
@@ -481,36 +501,32 @@ async def send(self, message: Data | Iterable[Data] | AsyncIterable[Data]) -> No
481
501
try :
482
502
# First fragment.
483
503
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
490
511
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
497
519
else :
498
520
raise TypeError ("async iterable must contain bytes or str" )
499
521
500
522
# Other fragments
501
523
async for chunk in achunks :
502
- if isinstance (chunk , str ) and text :
524
+ if isinstance (chunk , str ) and encode :
503
525
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 :
509
528
async with self .send_context ():
510
- self .protocol .send_continuation (
511
- chunk ,
512
- fin = False ,
513
- )
529
+ self .protocol .send_continuation (chunk , fin = False )
514
530
else :
515
531
raise TypeError ("async iterable must contain uniform types" )
516
532
0 commit comments