code: def _on_message_internal(self):
      # This is a hacky workaround for the test client's fundamentally synchronous nature
      attempt = 0
      try_count = 5
      while attempt < try_count:
          try:
              while True:
                  # increase to avoid races
                  message = self._ws._send_queue.get(timeout=0.01)
  
                  if isinstance(message, BaseException):
                      raise message
  
                  self._ws._raise_on_close(message)
  
                  if "text" in message:
                      self._on_message(message["text"])
                  if "bytes" in message:
                      self._on_message(message["bytes"])
  
          except Empty:
              attempt += 1
  
file_path: perspective/python/perspective/perspective/client/starlette_test.py
imports: from starlette.testclient import TestClient
  from starlette.websockets import WebSocketDisconnect
  from queue import Empty
  from .websocket import PerspectiveWebsocketClient, PerspectiveWebsocketConnection, Periodic
label_desc: Use image node_id 3 for calling the _PerspectiveStarletteWebsocketConnection obj's underlying member method code with example usage: obj._on_message_internal() without return types